marielle
marielle

Reputation: 438

Sum array of objects values by multiple keys in array

Suppose I have:

const arr = [
    {'name': 'P1','value': 150, 'nn': 2},
    {'name': 'P1','value': 150, 'nn': 3},
    {'name': 'P2','value': 200, 'nn': 5},
    {'name': 'P3','value': 450, 'nn': 1}
]
const keysToSum = ['value', 'nn' ]

and I want:

[
  { name: 'P1', value: 300, nn: 5 },
  { name: 'P2', value: 200, nn: 5 },
  { name: 'P3', value: 450, nn: 1 }
]

So I want to sum the values inside the keys value and nn (because these keys are in keysToSum) if name is the same. How can I do that?

I know I can use reduce, for example:

const result = arr.reduce((acc, val) => {
    const o = acc.filter((obj) => {
        return obj.name == val.name;
    }).pop() || {name: val.name, value: 0};
    
    o.value += val.value;
    acc.push(o);
    return acc;
},[])

But this piece of code works with only a key (value in that case), how can generalize it using keysToSum?

const arr = [
{'name': 'P1','value': 150, 'nn': 2},
{'name': 'P1','value': 150, 'nn': 3},
{'name': 'P2','value': 200, 'nn': 5},
{'name': 'P3','value': 450, 'nn': 1}
];

const result = arr.reduce((acc, val) => {
    const o = acc.filter((obj) => {
        return obj.name == val.name;
    }).pop() || {name: val.name, value: 0};
    
    o.value += val.value;
    acc.push(o);
    return acc;
},[]);

console.log(result)

Upvotes: 0

Views: 91

Answers (2)

epascarello
epascarello

Reputation: 207557

Using reduce with an object that uses the name as a key, you can easily keep track of the objecs with dupe and increment the properties.

const arr = [
    {'name': 'P1','value': 150, 'nn': 0},
    {'name': 'P1','value': 150, 'nn': 3},
    {'name': 'P2','value': 200, 'nn': 2},
    {'name': 'P3','value': 450, 'nn': 5}
]
const keysToSum = ['value', 'nn' ]

const summed = Object.values(arr.reduce((obj, record) => {
  // have we seen this name yet? If not copy the record
  if (!obj[record.name]) {
    obj[record.name] = { ...record };
  } else {
    // if we saw it, sum the fields we care about
    keysToSum.forEach(key => {
      obj[record.name][key] += record[key];
    })
  }
  return obj
}, {}));

console.log(summed)

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386868

You could reduce the array by using an object and iterate the wanted keys for summing property values.

const
    array = [{ name: 'P1', value: 150, nn: 0 }, { name: 'P1', value: 150, nn: 3 }, { name: 'P2', value: 200, nn: 2 }, { name: 'P3', value: 450, nn: 5 }],
    keysToSum = ['value', 'nn'],
    result = Object.values(array.reduce((r, { name, ...o }) => {
        if (!r[name]) r[name] = { name };
        keysToSum.forEach(k => r[name][k] = (r[name][k] || 0) + o[k]);
        return r;
    }, []));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Related Questions