Search code examples
javascriptarraysobjectmergegrouping

How to group and merge array entries and to sum-up values on multiple common (but not all) keys?


This thread had multiple approaches for summing values by common key, and one had a method to group by all common keys. I am looking for a more granular and controlled grouping. My data example is the following:

const arr = [
{'ID':'1','Parent':'1','Member': '1','Code': '123','Subject': 'Org A','value': 0.3},
{'ID':'2','Parent':'1','Member': '1','Code': '124','Subject': 'Org A','value': 0.25},
{'ID':'3','Parent':'1','Member': '1','Code': '123','Subject': 'Org B','value': 0.45},
{'ID':'4','Parent':'1','Member': '2','Code': '125','Subject': 'Org A','value': 0.8},
{'ID':'5','Parent':'1','Member': '2','Code': '211','Subject': 'Org C','value': 0.3},
{'ID':'6','Parent':'1','Member': '3','Code': '221','Subject': 'Org B','value': 0.3},
{'ID':'7','Parent':'1','Member': '3','Code': '221','Subject': 'Org C','value': 0.25},
{'ID':'8','Parent':'1','Member': '3','Code': '234','Subject': 'Org A','value': 0.45},
{'ID':'9','Parent':'1','Member': '4','Code': '123','Subject': 'Org A','value': 0.8},
{'ID':'10','Parent':'2','Member': '5','Code': '123','Subject': 'Org D','value': 0.3},
{'ID':'11','Parent':'2','Member': '5','Code': '123','Subject': 'Org E','value': 0.3},
{'ID':'12','Parent':'2','Member': '6','Code': '125','Subject': 'Org E','value': 0.25},
{'ID':'13','Parent':'2','Member': '6','Code': '211','Subject': 'Org F','value': 0.45},
{'ID':'14','Parent':'2','Member': '6','Code': '221','Subject': 'Org F','value': 0.8},
{'ID':'15','Parent':'2','Member': '6','Code': '123','Subject': 'Org G','value': 0.3},
{'ID':'16','Parent':'3','Member': '7','Code': '124','Subject': 'Org H','value': 0.3},
{'ID':'17','Parent':'3','Member': '8','Code': '124','Subject': 'Org H','value': 0.25},
{'ID':'18','Parent':'3','Member': '9','Code': '123','Subject': 'Org I','value': 0.45},
{'ID':'19','Parent':'3','Member': '10','Code': '123','Subject': 'Org J','value': 0.8},
{'ID':'20','Parent':'3','Member': '10','Code': '211','Subject': 'Org I','value': 0.3},
{'ID':'21','Parent':'4','Member': '11','Code': '221','Subject': 'Org K','value': 0.3},
{'ID':'22','Parent':'4','Member': '11','Code': '234','Subject': 'Org K','value': 0.25},
{'ID':'23','Parent':'4','Member': '12','Code': '234','Subject': 'Org K','value': 0.45},
{'ID':'24','Parent':'4','Member': '12','Code': '123','Subject': 'Org L','value': 0.8},
{'ID':'25','Parent':'4','Member': '13','Code': '211','Subject': 'Org M','value': 0.3}
];

The solutions from the other thread either group by 1 set column:

// Group by 1 column //

const summed = arr.reduce((acc, cur) => {
  const item = acc.length > 0 && acc.find(({
    Code
  }) => Code === cur.Code)
  if (item) {
    item.value += cur.value
  } else acc.push({Code:cur.Code,value:cur.value});
  return acc
}, [])
console.log(arr); // not modified
console.log(summed)

Or by all common values

// Group by multiple columns //

const res = Array.from(arr.reduce((acc, {value, ...r}) => {
  const key = JSON.stringify(r);
  const current = acc.get(key) || {...r, value: 0};  
  return acc.set(key, {...current, value: current.value + value});
}, new Map).values());
console.log(res);

I am trying to group by Parent and Code to sum the value.


Solution

  • Just test both properties in the find() callback, and add both to the new object when pushing into acc.

    const arr = [
    {'ID':'1','Parent':'1','Member': '1','Code': '123','Subject': 'Org A','value': 0.3},
    {'ID':'2','Parent':'1','Member': '1','Code': '124','Subject': 'Org A','value': 0.25},
    {'ID':'3','Parent':'1','Member': '1','Code': '123','Subject': 'Org B','value': 0.45},
    {'ID':'4','Parent':'1','Member': '2','Code': '125','Subject': 'Org A','value': 0.8},
    {'ID':'5','Parent':'1','Member': '2','Code': '211','Subject': 'Org C','value': 0.3},
    {'ID':'6','Parent':'1','Member': '3','Code': '221','Subject': 'Org B','value': 0.3},
    {'ID':'7','Parent':'1','Member': '3','Code': '221','Subject': 'Org C','value': 0.25},
    {'ID':'8','Parent':'1','Member': '3','Code': '234','Subject': 'Org A','value': 0.45},
    {'ID':'9','Parent':'1','Member': '4','Code': '123','Subject': 'Org A','value': 0.8},
    {'ID':'10','Parent':'2','Member': '5','Code': '123','Subject': 'Org D','value': 0.3},
    {'ID':'11','Parent':'2','Member': '5','Code': '123','Subject': 'Org E','value': 0.3},
    {'ID':'12','Parent':'2','Member': '6','Code': '125','Subject': 'Org E','value': 0.25},
    {'ID':'13','Parent':'2','Member': '6','Code': '211','Subject': 'Org F','value': 0.45},
    {'ID':'14','Parent':'2','Member': '6','Code': '221','Subject': 'Org F','value': 0.8},
    {'ID':'15','Parent':'2','Member': '6','Code': '123','Subject': 'Org G','value': 0.3},
    {'ID':'16','Parent':'3','Member': '7','Code': '124','Subject': 'Org H','value': 0.3},
    {'ID':'17','Parent':'3','Member': '8','Code': '124','Subject': 'Org H','value': 0.25},
    {'ID':'18','Parent':'3','Member': '9','Code': '123','Subject': 'Org I','value': 0.45},
    {'ID':'19','Parent':'3','Member': '10','Code': '123','Subject': 'Org J','value': 0.8},
    {'ID':'20','Parent':'3','Member': '10','Code': '211','Subject': 'Org I','value': 0.3},
    {'ID':'21','Parent':'4','Member': '11','Code': '221','Subject': 'Org K','value': 0.3},
    {'ID':'22','Parent':'4','Member': '11','Code': '234','Subject': 'Org K','value': 0.25},
    {'ID':'23','Parent':'4','Member': '12','Code': '234','Subject': 'Org K','value': 0.45},
    {'ID':'24','Parent':'4','Member': '12','Code': '123','Subject': 'Org L','value': 0.8},
    {'ID':'25','Parent':'4','Member': '13','Code': '211','Subject': 'Org M','value': 0.3}
    ];
    
    const summed = arr.reduce((acc, cur) => {
      const item = acc.length > 0 && acc.find(({
        Code, Parent
      }) => Code === cur.Code && Parent == cur.Parent)
      if (item) {
        item.value += cur.value
      } else acc.push({
        Code: cur.Code,
        Parent: cur.Parent,
        value: cur.value
      });
      return acc
    }, [])
    console.log(arr); // not modified
    console.log(summed)