How to group by more than one key and sum its values in an array of objects, using Lodash?

Viewed 727

In the array below, I want to group by item and reason, then calculate the total quantity:

item_movements = [
  {
    item: "Apple",
    reason: 1,
    quantity: 5
  },
  {
    item: "Banana",
    reason: 2,
    quantity: 10
  },
  {
    item: "Apple",
    reason: 1,
    quantity: 5
  },
  {
    item: "Banana",
    reason: 1,
    quantity: 8
  },
  {
    item: "Apple",
    reason: 2,
    quantity: 3
  },
  {
    item: "Banana",
    reason: 1,
    quantity: 8
  }
];

The result I am trying to achieve is this:

item_totals = [
  {
    item: "Apple",
    1: 10,
    2: 3
  },
  {
    item: "Banana",
    1: 16,
    2: 10
  }
];

I was able to group by item using Loadash like so:

({
  itemMovementbyItem() {
    var result = _(this.item_movements)
      .groupBy(x => x.item)
      .map((value, key) => ({
        item: key,
        item_movements: value
      }))
      .value()

    return result
  }
})

But I am having problems to group a second time by reason.

2 Answers

You could sum the properies directly after grouping by 'item'.

function itemMovementbyItem(array) {
    return _(array)
        .groupBy('item')
        .map(group => ({
            item: group[0].item,
            1: _.sumBy(group, o => o.reason === 1 ? o.quantity : 0),
            2: _.sumBy(group, o => o.reason === 2 ? o.quantity : 0)
        }))
        .value();
}

var item_movements = [{ item: "Apple", reason: 1, quantity: 5 }, { item: "Banana", reason: 2, quantity: 10 }, { item: "Apple", reason: 1, quantity: 5 }, { item: "Banana", reason: 1, quantity: 8 }, { item: "Apple", reason: 2, quantity: 3 }, { item: "Banana", reason: 1, quantity: 8 }];

console.log(itemMovementbyItem(item_movements));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

Use reduce to build one object with keys as item. Get Object.values from object.

const item_movements = [
  {
    item: "Apple",
    reason: 1,
    quantity: 5
  },
  {
    item: "Banana",
    reason: 2,
    quantity: 10
  },
  {
    item: "Apple",
    reason: 1,
    quantity: 5
  },
  {
    item: "Banana",
    reason: 1,
    quantity: 8
  },
  {
    item: "Apple",
    reason: 2,
    quantity: 3
  },
  {
    item: "Banana",
    reason: 1,
    quantity: 8
  }
];

const all = item_movements.reduce((acc, { item, reason, quantity }) => {
  acc[item] =
    item in acc
      ? {
          ...acc[item],
          [reason]: (acc[item][reason] || 0) + quantity
        }
      : { item, [reason]: quantity };
  return acc;
}, {});

const item_totals = Object.values(all);

console.log(item_totals);

Related