Json conversion from one form to another - javascript

I want to convert this JSON to the format mentioned in the result section.
[
{ name: "FieldData[FirstName][Operator]", value: "=" }
{ name: "FieldData[FirstName][Value]", value: "test" }
{ name: "FieldData[LastName][Operator]", value: "=" }
{ name: "FieldData[LastName][Value]", value: "test" }
]
Result:
FieldData
{
FirstName {
Operator: =,
Value: tset
},
LastName {
Operator: =,
Value: tset
}
}

Using String.prototype.split function and regex, you can extract the strings inside [] and based on that data, you can generate the nested objects as follows.
const input = [
{name: "FieldData[FirstName][Operator]", value: "="},
{name: "FieldData[FirstName][Value]", value: "test"},
{name: "FieldData[LastName][Operator]", value: "="},
{name: "FieldData[LastName][Value]", value: "test"}
];
const result = input.reduce((acc, cur) => {
const keys = cur.name.split(/\[(.*?)\]/).filter((item) => item);
let item = acc;
for (let index = 0; index < keys.length - 1; index ++) {
const key = keys[index];
if (!(key in item)) {
item[key] = {};
}
item = item[key];
}
item[keys[keys.length - 1]] = cur.value;
return acc;
}, {});
console.log(result);

Using a regex and javascript's ability to set properties using obj[propname] = value as the same as obj.propname = value
var d = [
{ name: "FieldData[FirstName][Operator]", value: "=" },
{ name: "FieldData[FirstName][Value]", value: "test" },
{ name: "FieldData[LastName][Operator]", value: "=" },
{ name: "FieldData[LastName][Value]", value: "test" },
];
var r = {};
for (var i=0; i<d.length; ++i)
{
var parts = d[i].name.match(/(.*)\[(.*)\]\[(.*)\]/)
r[parts[1]] = r[parts[1]] || {};
r[parts[1]][parts[2]] = r[parts[1]][parts[2]] || {};
r[parts[1]][parts[2]][parts[3]] = d[i].value;
}
console.log(r)
(note: SO console JSONifies the object, look in the browser console for the object)

Related

filter and sum array of objects in javascript

Hello and thank you for checking my question.
I have an array of objects which contains multiple entries for the same person corresponding to different dates. I need to sum the values for each person.
const data = [
{
name: "Bob",
date: 3/27/22
value: 300
},
{
name: "Alice",
date: 1/13/22
value: 500
},
{
name: "Bob",
date: 5/13/22
value: 400
},
{
name: "Alice",
date: 4/19/22
value: 350
},
{
name: "John",
date: 2/15/22
value: 700
},
]
I need the result to be:
const result = [
{
name: "Bob",
value: 700
},
{
name: "Alice",
value: 850
},
{
name: "John",
value: 700
},
]
How can I do this in the most efficient way possible?
So far, I have only been able to achieve this by using the filter array method returning the name value, pushing the result to a new array, and summing that array. However, I do not know all of the name values in advance so this wont work.
Thank you for your time
I think this approach has reasonable performance :
const data = [
{
name: "Bob",
value: 300
},
{
name: "Alice",
value: 500
},
{
name: "Bob",
value: 400
},
{
name: "Alice",
value: 350
},
{
name: "John",
value: 700
},
];
const reduceData = (data) => data.reduce((acc, cur) => {
const {name, value} = cur; // Get name and value from current item
const item = acc.find(it => it.name === name); // Find in our accumulator the desired object
item ? item.value += value : acc.push({name, value}); // Update object or create a new object if it doesn't exist
return acc; // Return accumulator
} , []);
console.log(reduceData(data));
Array.reduce takes an internal function as first parameter.
This internal function usually takes two parameters: one will be the "accumulator" that will be returned by the function and the other will be the "current array item".
Array.reduce will run the internal function on every array item and finally return its value.
The "accumulator" parameter passed to the internal function on every run is the return value of the previous internal function call.
Array.reduce can have an additional parameter which is the initial accumulator value passed to the first internal function call (here we use an empty array).
More info here -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
One way to do this can be this:
const data = [
{
name: 'Bob',
date: '3/27/22',
value: 300,
},
{
name: 'Alice',
date: '1/13/22',
value: 500,
},
{
name: 'Bob',
date: '5/13/22',
value: 400,
},
{
name: 'Alice',
date: '4/19/22',
value: 350,
},
{
name: 'John',
date: '2/15/22',
value: 700,
},
];
let res = data.reduce((agg, curr) => {
agg[curr.name] = (agg[curr.name] || 0) + curr.value;
return agg;
}, {});
const res2 = Object.keys(res).map((v) => {
return {
name: v,
value: res[v],
};
});
console.log(res2);
You can do this by mapping also
const data = [
{
name: "Bob",
date: ' 3 / 27 / 22',
value: 1,
},
{
name: "Alice",
date: '1 / 13 / 22',
value: 2,
},
{
name: "Bob",
date: '5 / 13 / 22',
value: 1,
},
{
name: "Alice",
date: ' 4 / 19 / 22',
value: 2
},
{
name: "John",
date: '2 / 15 / 22',
value: 3
},
]
const res = {};
for (let i = 0; i < data.length; i++) {
if (res[data[i].name]) { //here if any key(name) is already present in res then we add the value in already present value of that name
res[data[i].name].value += data[i].value
} else {
res[data[i].name] = data[i] //here we are inserting data into res object if doesn't find any key with already name present in it
}
}
const res2 = Object.keys(res).map(person => {
return {
name: person,
value: res[person].value
}
})
console.log(res2);
You can merge the value with the same property of key name by using reduce function.
const mergedData = data.reduce((prev, curr) => {
if(prev[curr.name]) {
// Sum the value if the name matches for multiple objects
prev[curr.name].value = prev[curr.name].value + curr.value
} else {
// Return the actual objects if not matches any
prev[curr.name] = curr
}
return prev
}, {});
console.log(Object.values(mergedData))
The output would be:
[
{
"name": "Bob",
"date": "3/27/22",
"value": 700
},
{
"name": "Alice",
"date": "1/13/22",
"value": 850
},
{
"name": "John",
"date": "2/15/22",
"value": 700
}
]
To know more about reduce you can learn from the documentation here
This is an (probably flawed) attempt to compare the answers on this page for their speed. Whether it is helpful or not could depend greatly on what the actual datasets that are being processed look like. The data array here is a wild (probably wrong) guess at what a worst case scenario could be like.
Feel free to fiddle with it.
const rand_letter = () => String.fromCharCode(65+Math.floor(Math.random() * 26));
const data = Array.from({ length: 10000 }, () => ({ name: rand_letter() + rand_letter(), value: Math.floor(Math.random() * 1000) }));
const A = (data) => {
let name_map = new Map();
for(let i = 0; i < data.length; i++) {
const { name, value } = data[i];
name_map.set(name, (name_map.get(name) ?? 0) + value);
}
return [...name_map.entries()].map(([name, value]) => ({ name, value }));
};
const B = (data) => {
let name_map = {};
for(let i = 0; i < data.length; i++) {
const { name, value } = data[i];
name_map[name] = (name_map[name] ?? 0) + value;
}
return Object.entries(name_map).map(([name, value]) => ({ name, value }));
};
const C = (data) =>
Object.entries(
data.reduce((acc, { name, value }) => {
acc[name] = (acc[name] ?? 0) + value;
return acc;
}, {})
).map(([name, value]) => ({ name, value }));
const D = (data) =>
data.reduce((acc, cur) => {
const {name, value} = cur;
const item = acc.find(it => it.name === name);
item ? item.value += value : acc.push({name, value});
return acc;
}, []);
const time = (fn) => {
const iter = 100;
const t0 = performance.now();
for(let i = 0; i < iter; i++)
fn(data);
console.log('time for ' + fn.name + ' (milliseconds)', (performance.now() - t0) / iter);
};
[A, B, C, D].forEach(time);

Create nested object from serializeArray

I'd like to create this structure:
{
"officine_type": "Pharmacie",
"officine_value": 2002626,
"declared_lines": [
{
"ean": 3578835501148,
"qty": 1
},
{
"ean": 3578835502671,
"qty": 2
}
],
"other_value": "my other value"
}
From a serializeArray() with this output:
0: {name: 'declared_lines.0.ean', value: '3578835502244'}
1: {name: 'declared_lines.0.qty', value: '4'}
2: {name: 'declared_lines.1.ean', value: '3578835502220'}
3: {name: 'declared_lines.1.qty', value: '1'}
4: {name: 'declared_lines.2.ean', value: ''}
5: {name: 'declared_lines.2.qty', value: '0'}
6: {name: 'officine_type', value: 'Pharmacy'}
7: {name: 'officine_value', value: '2000461'}
8: {name: 'other_value', value: ''}
I'm struggling on how to push sub-objects in declared_lines
Right now i have this:
let formData = form.serializeArray();
for (let i = 0; i < formData.length; i++) {
if (formData[i]['name'].indexOf('declared_lines') !== 1) {
let inputName = formData[i]['name'].split('.');
let namespace = inputName[0];
let n = inputName[1];
let key = inputName[2];
let subObj = {};
let current = 'declared_lines['+i+']';
let previous = 'declared_lines['+(i-1)+']';
if (obj.hasOwnProperty(namespace) === false) {
obj[namespace] = [];
}
}
obj[formData[i]['name']] = formData[i]['value'];
}
My brain won't go further :(
You could take the name and split it by dot for the path of the new object and the value and build a new object with the given information.
In setValue, the reduce callback checks if the next key is a stringed numerical value and takes an array as default object instead of an object.
function setValue(object, path, value) {
const last = path.pop();
path
.reduce((o, k, i, kk) => o[k] ??= (isFinite(i + 1 in kk ? kk[i + 1] : last) ? [] : {}), object)
[last] = value;
return object;
}
const
data = [{ name: 'declared_lines.0.ean', value: '3578835502244' }, { name: 'declared_lines.0.qty', value: '4' }, { name: 'declared_lines.1.ean', value: '3578835502220' }, { name: 'declared_lines.1.qty', value: '1' }, { name: 'declared_lines.2.ean', value: '' }, { name: 'declared_lines.2.qty', value: '0' }, { name: 'officine_type', value: 'Pharmacy' }, { name: 'officine_value', value: '2000461' }, { name: 'other_value', value: '' }],
result = data.reduce(
(object, { name, value }) => setValue(object, name.split('.'), value),
{}
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Object destructuring and array.reduce can make your code more readable, try:
let formData = [
{name: 'declared_lines.0.ean', value: '3578835502244'},
{name: 'declared_lines.0.qty', value: '4'},
{name: 'declared_lines.1.ean', value: '3578835502220'},
{name: 'declared_lines.1.qty', value: '1'},
{name: 'declared_lines.2.ean', value: ''},
{name: 'declared_lines.2.qty', value: '0'},
{name: 'officine_type', value: 'Pharmacy'},
{name: 'officine_value', value: '2000461'},
{name: 'other_value', value: ''}
];
let output = formData.reduce((acc,cur) => {
let { name, value } = cur;
if(name.indexOf('declared_lines') === -1){
acc[name] = value;
} else {
let [namespace, n, key] = name.split('.');
if(!acc[namespace]) acc[namespace] = [];
if(!acc[namespace][n]) acc[namespace][n] = {};
acc[namespace][n][key] = value;
}
return acc;
}, {});
console.log(output);
In this case reduce starts with an empty object and it loops over your array to process each element (cur).

Javascript - How to combine all combinations into an array of objects

I have the following array:
[{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue'
}]
Using some javascript logic I would like to output the following array:
[{
option1: 10,
option2: 'red'
},
{
option1: 10,
option2: 'blue'
},
{
option1: 12,
option2: 'red'
},
{
option1: 12,
option2: 'blue'
}]
What is the best and correct way to achieve this using javascript?
Lets say your first array is named arr.
var arr = [{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue'
}];
var v1 = arr[0].values.split(',');
var v2 = arr[1].values.split(',');
var res = new Array();
for(i in v1){
for(j in v2){
res.push({'option1':v1[i],'option2':v2[j]});
}
}
console.log(res);
Here's an approach that can handle an arbitrary number of objects.
function valuesCrossProduct(input) {
return input.flatMap((current, index, array) => {
let result = [];
let values = current.values.split(',');
for (let v of values) {
for (let i = 0; i < array.length; i++) {
if (i <= index) {
// Skip creating cross products with self (i.e. == index)
// and with previously visited objects (i.e. < index).
continue;
}
let iValues = array[i].values.split(',');
let currentKey = `option${index}`;
let iKey = `option${i}`;
for (let iv of iValues) {
result.push({
[currentKey]: v,
[iKey]: iv,
});
}
}
}
return result;
});
}
let twoElementArray = [{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue',
}];
let threeElementArray = [{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue',
},
{
name: 'baz',
values: 'wham,bam',
}];
console.log(valuesCrossProduct(twoElementArray));
console.log(valuesCrossProduct(threeElementArray));
Functional for the win.
Note: as it is, this only works for an array of two objects, with any number of values in each, where the first set of values are numbers and the second set are strings, which is what you described above.
const arr = [{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue'
}];
const values = arr
.map(o => o.values.split(','))
.reduce((cur, next) => {
return cur.map(c => {
return next.map(n => {
return {
option1: parseInt(c),
option2: n
};
});
}).flat();
});
console.log(values);
If you need generic approach to get possible options from various values.
const options = data => {
let sets = [[]];
data.forEach(({ values }, i) => {
const new_set = [];
values.split(",").forEach(value => {
new_set.push(
Array.from(sets, set => [...set, [`option${i + 1}`, value]])
);
});
sets = new_set.flatMap(set => set);
});
return sets.map(set => Object.fromEntries(set));
};
const data = [
{
name: "foo",
values: "10,12"
},
{
name: "bar",
values: "red,blue,green"
},
{
name: "test",
values: "top,bottom"
}
];
console.log(options(data));

How to merge 3 javascript object with modified value

I have 3 objects like
[
const arr = [
{name:'ABC', value:123},
{name:'ABC', value:456},
{name:'ABC',value:789},
{name:'DEF',value:9999},
name:'DEF', value:0000}
]
i want output like
updatedArr = [
{name:'ABC', value:123, value1:456, value2:789}
{name:'DEF', value:9999, value1:0000}
]
any kind of links regarding this will be also helpful.
You could use reduce method to create an object and then Object.values to get an array of values.
const arr = [{name:'ABC', value:123},{name:'ABC', value:456},{name:'ABC',value:789},{name:'DEF',value:9999},{name:'DEF', value:0000}]
const res = arr.reduce((r, e) => {
if(!r[e.name]) r[e.name] = {...e}
else {
let {name, ...rest} = r[e.name];
r[e.name][`value${Object.keys(rest).length}`] = e.value
}
return r;
}, {});
console.log(Object.values(res))
const arr = [{
name: 'ABC',
value: 123
},
{
name: 'ABC',
value: 456
},
{
name: 'ABC',
value: 789
},
{
name: 'DEF',
value: 9999
},
{
name: 'DEF',
value: 0000
}
]
const res = Object.values(arr.reduce((acc, item) => {
if (!acc[item.name]) {
acc[item.name] = item;
} else {
acc[item.name]['value' + (Object.keys(acc[item.name]).length - 1)] = item.value;
}
return acc;
}, {}));
console.log(res)
use object assignation:
Object.assign(ob1,ob2);

How to convert an array to an object in Lodash?

I have:
{
dis:{["String1","String2","String3"]},
par:"pony"
}
And I want to turn it into this:
[
{ name: 'String1', value: "pony" },
{ name: 'String2', value: "pony" },
{ name: 'String3', value: "pony" }
]
If you change to valid js data you can do this with reduce()
var obj = {
dis: ["String1", "String2", "String3"],
par: "pony"
}
var result = obj.dis.reduce(function(r, e) {
r.push({name: e, value: obj.par});
return r;
}, []);
console.log(result)
You can do it easily, but beware you have double parentheses (mustaches and square bracket).
var
data = {
dis: ["String1","String2","String3"],
par: "pony"
},
result = [];
for (var index in data.dis)
result.push({ name: data.dis[index], value: data.par}
you can find the fiddle here.
You can do it using Array.protoype.map:
var obj = {
dis:["String1","String2","String3"],
par:"pony"
};
var arrOfObj = obj.dis.map(function(name) {
return {
name: name,
value: obj.par
}
});
console.log(arrOfObj)
Or lodash's _.map:
var obj = {
dis:["String1","String2","String3"],
par:"pony"
};
var arrOfObj = _.map(obj.dis, function(name) {
return {
name: name,
value: obj.par
}
});
console.log(arrOfObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>

Categories