-3

I have an array of objects, where I want to find the objects having same value for name property and then merge them into single object by adding their values & subvalues.

const arr = [
 { id: 1, name: 'a', value: 2, subvalue: 3 }, 
 { id: 2, name: 'b', value: 4, subvalue: 3 },
 { id: 3, name: 'c', value: 4, subvalue: 3 },
 { id: 4, name: 'b', value: 3, subvalue: 4 } 
]

Expected output

[
 { id: 1, name: 'a', value: 2, subvalue: 3 }, 
 { id: 2, name: 'b', value: 7, subvalue: 7 },
 { id: 3, name: 'c', value: 4, subvalue: 3 }
]

The id of the new object can either be 2 or it can be 4. So it doesn't matter much. I have tried using filter, but not able to find a solution which suits my requirement

marukobotto
  • 759
  • 3
  • 12
  • 26
  • Using `filter` alone won't solve this task, see my answer! – XMehdi01 May 01 '23 at 08:57
  • Does this answer your question? [Most efficient method to groupby on an array of objects](https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects) – pilchard May 01 '23 at 09:56
  • or [Javascript array of objects group and sum items](https://stackoverflow.com/questions/65059071/javascript-array-of-objects-group-and-sum-items) or [Grouping an Array and then summing values](https://stackoverflow.com/questions/68339586/grouping-an-array-and-then-summing-values) or [How to group by and sum an array of objects javascript?](https://stackoverflow.com/questions/72314939/how-to-group-by-and-sum-an-array-of-objects-javascript) – pilchard May 01 '23 at 09:59

2 Answers2

2

you can use reduce to group by name and then get the values array of the grouped object using Object.values

const arr = [
 { id: 1, name: 'a', value: 2, subvalue: 3 }, 
 { id: 2, name: 'b', value: 4, subvalue: 3 },
 { id: 3, name: 'c', value: 4, subvalue: 3 },
 { id: 4, name: 'b', value: 3, subvalue: 4 } 
]

const res = Object.values(arr.reduce((acc,curr) => {
  if(!acc[curr.name]){
    acc[curr.name] = curr
  } else {
    acc[curr.name].value += curr.value
    acc[curr.name].subvalue += curr.subvalue
  }
  return acc
},{}))

console.log(res)

or the whole if else logic be one-lined but less readable

const arr = [
 { id: 1, name: 'a', value: 2, subvalue: 3 }, 
 { id: 2, name: 'b', value: 4, subvalue: 3 },
 { id: 3, name: 'c', value: 4, subvalue: 3 },
 { id: 4, name: 'b', value: 3, subvalue: 4 } 
]

const res = Object.values(arr.reduce((acc, curr) => {
    acc[curr.name] = acc[curr.name] ? {...acc[curr.name], value: acc[curr.name].value + curr.value, subvalue: acc[curr.name].subvalue + curr.subvalue} : curr
    return acc
}, {}))

console.log(res)
cmgchess
  • 7,996
  • 37
  • 44
  • 62
1

Using only Array#reduce, see if the name of the current object is already present in the accumulator with Array#findIndex, then Values and Subvalues should be accumulated respectively with the current object Values and Subvalues, Otherwise add the current object in the accumulator:

const arr = [ { id: 1, name: 'a', value: 2, subvalue: 3 }, { id: 2, name: 'b', value: 4, subvalue: 3 }, { id: 3, name: 'c', value: 4, subvalue: 3 }, { id: 4, name: 'b', value: 3, subvalue: 4 } ];
const result = arr.reduce((acc, cur) => {
  const index = acc.findIndex((obj) => obj.name === cur.name);
  if (index !== -1) {
    acc[index] = {
      ...acc[index],
      value: acc[index].value + cur.value,
      subvalue: acc[index].subvalue + cur.subvalue,
    };
  } else {
    acc.push(cur);
  }
  return acc;
}, []);
console.log(result);

if you don't want to maintain the first (original) id and would rather use the id from the last (most recent) merged object, just replace ...acc[index] with ...cur when you merge them.

XMehdi01
  • 5,538
  • 2
  • 10
  • 34