Search code examples
javascriptecmascript-6reducehigher-order-functionsside-effects

Sum values of nested objects without side-effects (reduce)


I'm trying to sum nutritional values nested inside food objects:

[
  {
    id: 'Potato',
    nutrition: [
      { id: 'PROT', val: 4 },
      { id: 'SUGAR', val: 1 }
    ]
  },
  {
    id: 'Banana',
    nutrition: [
      { id: 'FIB', val: 10 },
      { id: 'SUGAR', val: 4 }
    ]
  }
]

I use Array.prototype.reduce() to do so:

foods.reduce((acc, { nutrition }) => {
    nutrition.map((x) => {
      let current = acc.find((y) => y.id === x.id)
      if (current) current.val += x.val
      else acc.push(x)
    })
  return acc
}, [])

This works, but it manipulates my original array and I cannot figure out why. I have tried to use the spread-operator and let/var but to no avail. How can I achieve what I want without side-effects?


Solution

  • You can make use of reduce and forEach and take Object.values in last, something like this:

    const foods = [ { id: 'Potato', nutrition: [ { id: 'PROT', val: 4 }, { id: 'SUGAR', val: 1 } ] }, { id: 'Banana', nutrition: [ { id: 'FIB', val: 10 }, { id: 'SUGAR', val: 4 } ] }];
    
    const result = Object.values(foods.reduce((a,{nutrition})=>{
        nutrition.forEach(({id, val})=>{
            (a[id] ??= {id, val:0}).val+=val;
        });
        return a;
    },{}));
    
    console.log(result);