Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reduce array by its items key in ES6

I've got array of fruits:

const fruits = [
  { name: 'apple', count: 2 },
  { name: 'apple', count: 4 },
  { name: 'banana', count: 5 },
  { name: 'apple', count: 3 },
  { name: 'apple', count: 4 },
  { name: 'apple', count: 1 },
  { name: 'banana', count: 3 },
  { name: 'apple', count: 1 },
  { name: 'orange', count: 2 }
];

What I try to achieve is to reduce this array so it look like this:

let fruitsReduces = [
  { name: 'apple', count: 15 },
  { name: 'banana', count: 8 },
  { name: 'orange', count: 2 }
];

I've done this using very ugly for loop:

for (let i = 0; i < fruits.length; i++) {
  fruitItem = {};
  fruitItem.name = fruits[i].name;
  fruitItem.count = fruits[i].count;

  const fruitAlreadyInTheArray = fruitsReduced.find(fruit => fruit.name === fruitItem.name);
  if (!fruitAlreadyInTheArray) {
    fruitsReduced.push(fruitItem);
  } else {
    fruitAlreadyInTheArray.count += fruitItem.count;
  }
}

But I'm sure that same thing can be done somehow by using ES6 array.reduce. Can you help me? I looked at JS (ES6): Reduce array based on object attribute but I can't figure it out.

like image 465
arielkuzminski Avatar asked Jun 24 '18 15:06

arielkuzminski


2 Answers

You can us reduce() the array into an object. Use Object.values() to convert the object into an array.

const fruits = [{"name":"apple","count":2},{"name":"apple","count":4},{"name":"banana","count":5},{"name":"apple","count":3},{"name":"apple","count":4},{"name":"apple","count":1},{"name":"banana","count":3},{"name":"apple","count":1},{"name":"orange","count":2}];

var result = Object.values(fruits.reduce((c, {name,count}) => {
  c[name] = c[name] || {name,count: 0};
  c[name].count += count;
  return c;
}, {}));

console.log(result);
like image 75
Eddie Avatar answered Oct 14 '22 07:10

Eddie


You could use an array as accumulator and seach for an inserted object with a wanted name for adding the actual count. If not found add a new object.

const
    fruits = [{ name: 'apple', count: 2 }, { name: 'apple', count: 4 }, { name: 'banana', count: 5 }, { name: 'apple', count: 3 }, { name: 'apple', count: 4 }, { name: 'apple', count: 1 }, { name: 'banana', count: 3 }, { name: 'apple', count: 1 }, { name: 'orange', count: 2 }],
    result = fruits.reduce((r, { name, count }) => {
        var item = r.find(o => o.name === name);
        if (!item) {
            item = { name, count: 0 };
            r.push(item);
        }
        item.count += count;
        return r;
    }, []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

A different approach by collecting all counts first with a Map and render the wanted array with Array.from and a mapping function which builds new objects.

const
    fruits = [{ name: 'apple', count: 2 }, { name: 'apple', count: 4 }, { name: 'banana', count: 5 }, { name: 'apple', count: 3 }, { name: 'apple', count: 4 }, { name: 'apple', count: 1 }, { name: 'banana', count: 3 }, { name: 'apple', count: 1 }, { name: 'orange', count: 2 }],
    result = Array.from(
        fruits.reduce(
            (m, { name, count }) => m.set(name, (m.get(name) || 0) + count),
            new Map
        ),
        ([name, count]) => ({ name, count })
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 31
Nina Scholz Avatar answered Oct 14 '22 08:10

Nina Scholz