Mavys
Mavys

Reputation: 33

Sum data from two array of objects

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

Answers (2)

Ori Drori
Ori Drori

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

ggorlen
ggorlen

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

Related Questions