0

I asked a similar question earlier today, but the output of this question is different. I asked wrongly the first time :(

I have a large array, which I have simplified for the question purpose

[
  {"item1": 3, "item40": 2},
  {"item1": 4, "item40": 8}
]

I would like to end up with this, which is the sum of each similar property in the array of objects. I tried a lot. Doing a forEach whith a forIn inside. But I am stuck. Please help :)

[7, 10]

Johansrk
  • 4,809
  • 3
  • 32
  • 32
  • Afaik, the order of properties in JS objects is not always guaranteed. Therefor you can't be 100% sure in your example ([7,10]) which is the sum of item1 and which is the sum of item40. You could either use a Map or retain the props during summation. See https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order – c_froehlich Jan 14 '21 at 19:06

4 Answers4

1

let arr = [
    {"item1": 3, "item40": 2},
    {"item1": 4, "item40": 8}
  ]
  
const res = Object.keys(arr[0]).map(key => arr.reduce((acc, cur) => acc + parseInt(cur[key] || 0), 0))
  
console.log(res)
ecoplaneteer
  • 1,555
  • 1
  • 4
  • 23
1

You first need to get a set of keys across all the data items, as the order and appearance of each key may differ from each item in the data array.

Then you can map the keys to a reduction across all the items.

const data = [ { "item1": 3, "item40": 2 }, { "item1": 4, "item40": 8 } ];

const keys = [...new Set(data.reduce((keys, obj) =>
  [...keys, ...Object.keys(obj)], []))];
  
const summary = keys.map((key) =>
  data.reduce((acc, item) => acc + (item[key] || 0), 0));

console.log(summary); // [ 7, 10 ]

It is better to retain the key-value pairs though...

const data = [ { "item1": 3, "item40": 2 }, { "item1": 4, "item40": 8 } ];

const keys = [...new Set(data.reduce((keys, obj) =>
  [...keys, ...Object.keys(obj)], []))];
  
const summary = keys.map((key) => ({
  key,
  value: data.reduce((acc, item) => acc + (item[key] || 0), 0)
}));

// [{"key": "item1", "value": 7}, {"key": "item40", "value": 10}]
console.log(summary);

Update Here is a better version. You reduce the items, and for each item, you reduce the keys.

const data = [ { "item1": 3, "item40": 2 }, { "item1": 4, "item40": 8 } ];

const keys = [...new Set(data.reduce((keys, obj) =>
  [...keys, ...Object.keys(obj)], []))];

const summary = data.reduce((values, item) =>
  keys.reduce((acc, key) =>
    ({ ...acc, [key]: acc[key] + (item[key] || 0)})
  , values
, {}));

console.log(summary); // { "item1": 7, "item40": 10 }
console.log(Object.values(summary)); // [ 7, 10 ]
Mr. Polywhirl
  • 31,606
  • 11
  • 65
  • 114
0

If you're open to external modules, you can do this quite nicely with lodash's _.sumBy:

const data = [
  { item1: 3, item2: 4, item40: 2 },
  { item1: 4, item2: 4, item40: 8 },
];

let output = {} // final result stored here
let keys = _.keys(data[0]) // or Object.keys(data[0]) // using the first object as a template to see what keys to search for
keys.forEach(key => output[key] = _.sumBy(data, key)) // sum them up

document.write(`<pre>${JSON.stringify(output, null, 4)}</pre>`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

It doesn't matter whether objects past data[0] are missing keys or have additional ones, but note that only the keys found in data[0] will be summed

marsnebulasoup
  • 2,030
  • 2
  • 10
  • 28
0

Here's a version that uses Map to maintain the sequence of the object properties:

const data = [{
    item1: 3,
    item40: 2
  },
  {
    item1: 4,
    item40: 8
  },
];

const arr =

  // Array.from converts iterator from values() below to array
  Array.from(

    data
    .reduce((map, obj) => {

      // accumulate each object's values into map
      Object.entries(obj).forEach(([propName, value]) => {
        map.set(propName, (map.get(propName) ? ? 0) + value);
      });

      return map;
    }, new Map())

    // extract all values from map into an iterator
    .values()
  );

console.log(arr); // [ 7, 10 ]
terrymorse
  • 5,564
  • 1
  • 14
  • 19