3

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.

Mavys
  • 33
  • 4

2 Answers2

6

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)
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
2

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));
ggorlen
  • 44,755
  • 7
  • 76
  • 106
  • Thanks for the answer, but I found that the ID went missing.. But then I also found a way to show the ID again, by adding if (k == "id") { a[e.id][k] = a[e.id][k] ? a[e.id][k] : e[k]; } right after the !== id if end -- edit, just figured out that I cannot use html markdown in the comment – Mavys Jun 09 '19 at 17:35