Sum similar keys in an array of objects
Solution 1:
First iterate through the array and push the 'name' into another object's property. If the property exists add the 'value' to the value of the property otherwise initialize the property to the 'value'. Once you build this object, iterate through the properties and push them to another array.
Here is some code:
var obj = [
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P2', 'value': 200 },
{ 'name': 'P3', 'value': 450 }
];
var holder = {};
obj.forEach(function(d) {
if (holder.hasOwnProperty(d.name)) {
holder[d.name] = holder[d.name] + d.value;
} else {
holder[d.name] = d.value;
}
});
var obj2 = [];
for (var prop in holder) {
obj2.push({ name: prop, value: holder[prop] });
}
console.log(obj2);
Hope this helps.
Solution 2:
An ES6 approach to group by name
:
You can convert your array of objects to a Map by using .reduce()
. The Map has key-value pairs, where each key is the name
, and each value
is the accumulated sum of values for that particular name
key. You can then easily convert the Map back into an array using Array.from()
, where you can provide a mapping function that will take the keys/values of the Map and convert them into objects:
const arr = [ { 'name': 'P1', 'value': 150 }, { 'name': 'P1', 'value': 150 }, { 'name': 'P2', 'value': 200 }, { 'name': 'P3', 'value': 450 } ];
const res = Array.from(arr.reduce(
(m, {name, value}) => m.set(name, (m.get(name) || 0) + value), new Map
), ([name, value]) => ({name, value}));
console.log(res);
Grouping by more than just name
:
Here's an approach that should work if you have other overlapping properties other than just name
(the keys/values need to be the same in both objects for them to "group"). It involves iterating through your array and reducing it to Map which holds key-value pairs. Each key of the new Map is a string of all the property values you want to group by, and so, if your object key already exists then you know it is a duplicate, which means you can add the object's current value to the stored object. Finally, you can use Array.from()
to transform your Map of key-value pairs, to an array of just values:
const arr = [{'name':'P1','value':150},{'name':'P1','value':150},{'name':'P2','value':200},{'name':'P3','value':450}];
const res = Array.from(arr.reduce((acc, {value, ...r}) => {
const key = JSON.stringify(r);
const current = acc.get(key) || {...r, value: 0};
return acc.set(key, {...current, value: current.value + value});
}, new Map).values());
console.log(res);
Solution 3:
You can use Array.reduce() to accumulate results during each iteration.
var arr = [{'name':'P1','value':150},{'name':'P1','value':150},{'name':'P2','value':200},{'name':'P3','value':450}];
var result = arr.reduce(function(acc, val){
var o = acc.filter(function(obj){
return obj.name==val.name;
}).pop() || {name:val.name, value:0};
o.value += val.value;
acc.push(o);
return acc;
},[]);
console.log(result);
Solution 4:
One more solution which is clean, I guess
var obj = [
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P2', 'value': 200 },
{ 'name': 'P3', 'value': 450 }
];
var result = [];
Array.from(new Set(obj.map(x => x.name))).forEach(x => {
result.push(obj.filter(y => y.name === x).reduce((output,item) => {
let val = output[x] === undefined?0:output[x];
output[x] = (item.value + val);
return output;
},{}));
})
console.log(result);
if you need to keep the object structure same than,
var obj = [
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P1', 'value': 150 },
{ 'name': 'P2', 'value': 200 },
{ 'name': 'P2', 'value': 1000 },
{ 'name': 'P3', 'value': 450 }
];
let output = [];
let result = {};
let uniqueName = Array.from(new Set(obj.map(x => x.name)));
uniqueName.forEach(n => {
output.push(obj.filter(x => x.name === n).reduce((a, item) => {
let val = a['name'] === undefined? item.value:a['value']+item.value;
return {name:n,value:val};
},{})
);
});
console.log(output);
Solution 5:
For some reason when I ran the code by @Vignesh Raja, I obtained the "sum" but also items duplicated. So, I had to remove the duplicates as I described below.
Original array:
arr=[{name: "LINCE-01", y: 70},
{name: "LINCE-01", y: 155},
{name: "LINCE-01", y: 210},
{name: "MIRAFLORES-03", y: 232},
{name: "MIRAFLORES-03", y: 267}]
Using @VigneshRaja's code:
var result = arr.reduce(function(acc, val){
var o = acc.filter(function(obj){
return obj.name==val.name;
}).pop() || {name:val.name, y:0};
o.y += val.y;
acc.push(o);
return acc;
},[]);
console.log(result);
First outcome:
result: [{name: "LINCE-01", y: 435},
{name: "LINCE-01", y: 435},
{name: "LINCE-01", y: 435},
{name: "MIRAFLORES-03", y: 499},
{name: "MIRAFLORES-03", y: 499}]
Removing the duplicates:
var finalresult = result.filter(function(itm, i, a) {
return i == a.indexOf(itm);
});
console.log(finalresult);
Finally, I obtained what I whished:
finalresult = [{name: "LINCE-01", y: 435},
{name: "MIRAFLORES-03", y: 657}]
Regards,