Use .reduce to sum object variabele based on object enum

Tags: , ,



I’m trying to use the .reduce method to sum the amount of different Expense objects. These Expenses can be of different type, and I’d like to keep the objects in the array splitted by that. So for example, the following array of expenses:

[
  {type: A, amount: 2},
  {type: A, amount: 1},
  {type: B, amount: 2},
  {type: A, amount: 3},
  {type: C, amount: 2},
  {type: B, amount: 1}
]

Should become this:

[
  {type: A, amount: 6},
  {type: B, amount: 3},
  {type: C, amount: 2}
]

Also note that there should not be amount: 0 if no expenses of that type are present, but just an expense less. So without expenses of type C, the result should look like:

[
  {type: A, amount: 6},
  {type: B, amount: 3}
]

This is how far I got:

    private joinExpenses(expenses: Expense[]): Expense[] {
    // Add all expenses amount splitted by payment type and store in object
    const expenseArrayAsObject = expenses.reduce(
        (expensesObject, item) => {
            const type = item.type;
            if (!expensesObject.hasOwnProperty(type)) {
                expensesObject[type] = {
                    type: type,
                    amount: {
                        amount: 0
                    } as Money
                } as Expense;
            }

            const expense: Expense = expensesObject[type];
            expense.amount.amount = expense.amount.amount + item.amount.amount;
            expensesObject[type] = expense;

            return expensesObject;
        },
        {} as { [key: string]: any }
    );

    // Convert object to array
    const joinedExpenses: Expense[] = [];
    for (const key in expenseArrayAsObject) {
        joinedExpenses.push(expenseArrayAsObject[key]);
    }

    return joinedExpenses;
}

This works, but I feel like mapping to the object first and converting that to an array is a step too much, and can be simplified. I can do some manipulation afterwards but I feel like I iterate through arrays too much.. I just don’t see how. Can you help me?

Answer

You can use as the following with .reduce() and .find() combination:

const data = [
  {type: 'A', amount: 2},
  {type: 'A', amount: 1},
  {type: 'B', amount: 2},
  {type: 'A', amount: 3},
  {type: 'C', amount: 2},
  {type: 'B', amount: 1}
];

const result = data.reduce((a, c) => {
  const found = a.find(e => e.type === c.type);
  if (found) found.amount = found.amount + c.amount;      
  return found ? a : a.concat(c);
}, []);

console.log(result);


Source: stackoverflow