Search code examples
javascriptarrayslodashjavascript-objects

How to reduce an array of objects by key and sum its values in javascript


I am trying to turn this:

var crates = [{category: "fruits", type: "apple", color: "green", number: 1}, 
             {category: "fruits", type: "apple", color: "red", number: 1},
             {category: "fruits", type: "banana", color: "yellow", number: 1}, 
             {category: "vegetables", type: "onion", color: "white", number: 1}]

into this:

var stand = [{category: "fruits", type: "apple", sum: 2}, 
             {category: "fruits", type: "banana", sum: 1}, 
             {category: "vegetables", type: "onion", sum: 1}]

using lodash/fp. So far I have tried a lot, this is the closest I managed to get:

var stand = flow(
      groupBy('type'),
      map((objs, key) => ({
          'category': key,
          'type': key,
          'sum': _.sumBy(objs, 'number') }))
    )(crates);

which results in:

[{category: undefined, type: undefined, sum: 2},
{category: undefined, type: undefined, sum: 1}
{category: undefined, type: undefined, sum: 1}]

So obviuously I don't get how to pass reference to the "category" and "type" values to the map function.

I'm new into lodash/fp and struggling with the whole fp concept, so I would be happy for anything that might point me in the right direction!


Solution

  • In lodash-fp methods have a fixed arity of 1 (callback receives 1 argument) to support auto-currying. This means that the map's callback doesn't get the key. You can get the type, and the category from the 1st object in the group.

    To allow currying the sumBy() parameters are switched, so the objs should be the 2nd param:

    const { flow, groupBy, map, sumBy } = _;
    
    const crates = [{"category":"fruits","type":"apple","color":"green","number":1},{"category":"fruits","type":"apple","color":"red","number":1},{"category":"fruits","type":"banana","color":"yellow","number":1},{"category":"vegetables","type":"onion","color":"white","number":1}];
    
    const result = flow(
      groupBy('type'),
      map((objs) => ({
        'category': objs[0].category,
        'type': objs[0].type,
        'sum': sumBy('number', objs)
      }))
    )(crates);
    
    console.log(result);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash-fp/0.10.4/lodash-fp.js"></script>