XanderTEM
XanderTEM

Reputation: 109

Creating an array of objects from an array JS

I have an array of objects. I want to reduce this array of objects based on the property category, so that I can get a total sum of each category. I wrote this function which returns an object:

const expenses =

 [{
      title: 'burger',
      sum: 57,
      category: 'eating_out',
      notes: 'was good',
    },
    {
      title: 'water bill',
      sum: 142.81,
      category: 'utilities',
      notes: 'this sucks',
    },
    {
      title: 'electricity bill',
      sum: 112.90,
      category: 'utilities',
      notes: 'could be worse',
    }]
    
    
    const totalExpensesByCategory = expenses.reduce(
    (acc, curr) => {
      if (!acc[curr.category]) {
        acc[curr.category] = curr.sum;
      } else {
        acc[curr.category] = +acc[curr.category] + curr.sum;
      }
      return acc;
    },
    {}
  );
  
  console.log(totalExpensesByCategory)

I want it to be an iterable object so that I can easily render components based on those entries.

How can I write it differently so that maybe it will return an array of objects that I can use map() on? Object.entries() doesn't seem like the ideal way to do it.

Upvotes: 1

Views: 297

Answers (3)

gog
gog

Reputation: 11347

Just use Maps instead of objects. That's what they are for. Unlike objects, Maps are iterable:

const totalExpensesByCategory = expenses.reduce(
    (m, curr) =>
        m.set(curr.category, (m.get(curr.category) ?? 0) + curr.sum),
    new Map
);

for (let [category, sum] of totalExpensesByCategory)
    console.log(category, sum)

As a bonus, your reducer looks much nicer without curly braces and return

Upvotes: 0

James
James

Reputation: 22247

Since you have valuable info in the object keys (the category) and similar in the values (the sum) you'll need both the key and the value from each object entry to create an array element. One approach is Object.entries, followed by map to create the individual objects that go into the output array.

const expenses =
  
 [{
      title: 'burger',
      sum: 57,
      category: 'eating_out',
      notes: 'was good',
    },
    {
      title: 'water bill',
      sum: 142.81,
      category: 'utilities',
      notes: 'this sucks',
    },
    {
      title: 'electricity bill',
      sum: 112.90,
      category: 'utilities',
      notes: 'could be worse',
    }]


const totalExpensesByCategory = Object.entries(expenses.reduce(
    (acc, curr) => {
      if (!acc[curr.category]) {
        acc[curr.category] = curr.sum;
      } else {
        acc[curr.category] = +acc[curr.category] + curr.sum;
      }
      return acc;
    },
    {}
  )).map(([k, v]) => ({category: k, sum: v}));
  
console.log(totalExpensesByCategory);

Upvotes: 3

adsy
adsy

Reputation: 11427

There's no reason not to build it up as an array of objects inside the reduce in the first place:

const totalExpensesByCategory = expenses.reduce(
    (acc, curr) => {
      const categoryIndex = acc.findIndex((category) => category.name === curr.category)
      if (categoryIndex === -1) {
        acc.push({name: curr.category, sum: curr.sum})
      } else {
        acc[categoryIndex].sum = acc[categoryIndex].sum + curr.sum;
      }
      return acc;
    },
    []
  );

Heres the output:

Output

Upvotes: 2

Related Questions