Reputation: 33
I have two array of objects, and I want to sum the object that have a same key (in this case, id), and if there is no match key, then just create a new one.. I'm sorry if I am not explaining it clearly, I am new to JavaScript/Array/Object thing...
var dataOne = [ { id:"1", total: 10, win: 5 }, { id:"2", total: 5, win: 1 }, { id:"3", total: 5, win: 2 } ]
and
var dataTwo = [ { id:"1", total: 5, win: 2 }, { id:"2", total: 2, win: 3 }, { id:"5", total: 5, win: 4 } ]
Expected result:
var combinedData = [ { id:"1", total: 15, win: 7 }, { id:"2", total: 7, win: 4 }, { id:"3", total: 5, win: 2 }, { id:"5", total: 5, win: 4 } ]
I have tried to use the solution from Sum all data in array of objects into new array of objects but, apparently the type of data is kind of different
So, I tried to use this method from Javascript - Sum of two object with same properties
function sumObjectsByKey(...objs) {
for (var prop in n) {
if (acc.hasOwnProperty(prop)) acc[prop] += n[prop];
else acc[prop] = n[prop];
}
return acc;
}
and
var combinedData = sumObjectsByKey(dataOne, dataTwo);
But apparently, that method won't work for an array of objects. I get
{0: "0[object Object][object Object]", 1: "0[object Object][object Object]", 2: "0[object Object][object Object]"}
as a result.
Upvotes: 3
Views: 2737
Reputation: 193145
Combine both using rest params and Array.flat()
. Reduce the items into a Map, while combining the current item, with the item that already exists in the Map. Convert the Map.values()
iterator to an array with Array.from()
:
const sumObjectsByKey = (sumFn, ...arrs) => Array.from(
arrs.flat() // combine the arrays
.reduce((m, o) => // retuce the combined arrays to a Map
m.set(o.id, // if add the item to the Map
m.has(o.id) ? sumFn(m.get(o.id), o) : { ...o } // if the item exists in Map, sum the current item with the one in the Map. If not, add a clone of the current item to the Map
)
, new Map).values()
)
// utility function to sum to object values (without the id)
const sumItem = ({ id, ...a }, b) => ({
id,
...Object.keys(a)
.reduce((r, k) => ({ ...r, [k]: a[k] + b[k] }), {})
});
const dataOne = [ { id:"1", total: 10, win: 5 }, { id:"2", total: 5, win: 1 }, { id:"3", total: 5, win: 2 } ]
const dataTwo = [ { id:"1", total: 5, win: 2 }, { id:"2", total: 2, win: 3 }, { id:"5", total: 5, win: 4 } ]
const result = sumObjectsByKey(sumItem, dataOne, dataTwo)
console.log(result)
Upvotes: 6
Reputation: 57383
I'd suggest Array.reduce
here. In the body of the reduction function, if the id
isn't in the result accumulator, add it, then increment all of the values for each property in the object.
var dataOne = [ { id:"1", total: 10, win: 5 }, { id:"2", total: 5, win: 1 }, { id:"3", total: 5, win: 2 } ]
var dataTwo = [ { id:"1", total: 5, win: 2 }, { id:"2", total: 2, win: 3 }, { id:"5", total: 5, win: 4 } ]
var sumObjectsByKey = (...objs) =>
Object.values(objs.reduce((a, e) => {
a[e.id] = a[e.id] || {id: e.id};
for (const k in e) {
if (k !== "id") {
a[e.id][k] = a[e.id][k] ? a[e.id][k] + e[k] : e[k];
}
}
return a;
}, {}))
;
console.log(sumObjectsByKey(...dataOne, ...dataTwo));
Upvotes: 2