4

I have an array of objects

const data = [
  { category: 'shopping', amount: 50 }, 
  { category: 'rent', amount: 1000 }, 
  { category: 'groceries', amount: 20 }, 
  { category: 'shopping', amount: 50 }
]

and I am trying to sum up the amounts per category

const result = [
  { category: 'shopping', amount: 100 },
  { category: 'rent', amount: 1000 },
  { category: 'groceries', amount: 20 }
]

So far, I'm thinking of removing the 'duplicates' of categories and store them into an array

const temp = data.map((obj) => {
  return obj.category
})

const categories = [...new Set(temp)] // ['shopping','rent','groceries']

With the above, i'm thinking of doing a nested loop but after many tries, I have failed.

Any help is appreciated

0

3 Answers 3

5

You can use reduce() to do that.

Iterate on given data and if an item with same category as current item exists, then add the amount, else add current item as a new entry.

const data = [
  { category: 'shopping', amount: 50 }, 
  { category: 'rent', amount: 1000 }, 
  { category: 'groceries', amount: 20 }, 
  { category: 'shopping', amount: 50 }
];

let result = data.reduce((acc, curr) => {
  let item = acc.find(item => item.category === curr.category);

  if (item) {
    item.amount += curr.amount;
  } else {
    acc.push(curr);
  }

  return acc;
}, []);

console.log(result);

Here is an alternative approach using object as the accumulator. This has a performance benefit as we wouldn't have to call find() on every iteration. Credits to frodo2975 for the suggestion.

const data = [
  { category: 'shopping', amount: 50 }, 
  { category: 'rent', amount: 1000 }, 
  { category: 'groceries', amount: 20 }, 
  { category: 'shopping', amount: 50 }
];

let result = Object.values(data.reduce((acc, curr) => {
  let item = acc[curr.category];

  if (item) {
    item.amount += curr.amount;
  } else {
    acc[curr.category] = curr;
  }

  return acc;
}, {}));

console.log(result);

Sign up to request clarification or add additional context in comments.

6 Comments

You can get better performance by accumulating into an object rather than an array, so that you don't have to call .find for every item. Then, you can use Object.entries to convert back to an array.
Thanks @frodo2975. Added this as an alternative in the answer.
Thanks guys, this helped. I haven't had much experience with the reduce method as it is a bit confusing @_@
You're welcome @LeonChung. I'm glad it helped. Yes, it would be confusing at first but you'll get better as you progress.
@Nikhil coming back to this, could you elaborate why I need the item variable? can't i create an object inside the function and push it directly into the accumulator? or am I understanding the method wrong
|
2

I would focus on efficiently computing the totals for each category, and then restructuring the data in the format that you need. So something like this:

// Construct an object mapping category to total amount.
const totals = data.reduce((totals, { category, amount }) => {
    totals[category] = (totals[category] || 0) + amount;
}, {});

// Format the data as you like.
const result = Object.entries(totals).map(([category, amount]) => ({ category, amount });

Comments

2

Just another version of using reduce method:)

const data = [
  { category: 'shopping', amount: 50 },
  { category: 'rent', amount: 1000 },
  { category: 'groceries', amount: 20 },
  { category: 'shopping', amount: 50 }
];

const result = data.reduce((a,c) => {
  a[c.category] = a[c.category] || {category: c.category, amount: 0};
  a[c.category].amount += c.amount;
  return a;
}, {})

console.log(Object.values(result));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.