2

Given the following array of objects:

myArray = [
  {
    item: 'Item 1',
    material: 'Material1',
    type: 'head'
  },
  {
    item: 'Item 1',
    material: 'Material1',
    type: 'head'
  },
  {
    item: 'Item 2',
    material: 'Material2',
    type: 'shell'
  },
  {
    item: 'Item 1',
    material: 'Material1',
    type: 'head'
  },
  {
    item: 'Item 2',
    material: 'Material2',
    type: 'shell'
  },
  {
    item: 'Item 3',
    material: 'Material3',
    type: 'support'
  },
  {
    item: 'Item 1',
    material: 'Material1',
    type: 'head'
  },
  {
    item: 'Item 3',
    material: 'Material3',
    type: 'support'
  },
  {
    item: 'Item 2',
    material: 'Material2',
    type: 'shell'
  }
]

I need to combine these by the item value with a count so that I get an array that looks like this:

var myResultArray = [
  {
    item: 'Item 1',
    material: 'Material1',
    type: 'head'
    count: 4
  },
  {
    item: 'Item 2',
    material: 'Material2',
    type: 'shell'
    count: 3
  },
  {
    item: 'Item 3',
    material: 'Material3',
    type: 'support'
    count: 2
  },
]

What is the easiest way to do this? I'm partial to Lodash, but I'm open to other alternatives. With _.groupBy() I can get everything grouped together with the item as the object key:

var myGrouped = _.groupBy(myArray, 'item');

but that only gets me part way there. Searching around here I see a lot of uses of _.reduce() (or just plain .reduce()) or _.map(), but I haven't been able to get my head around exactly how to use them in this case. If I try to use _.groupBy() chained with _.map() like so

var myGrouped = _(myArray).groupBy('item').map(function(item) {
                  // do stuff here
                });

the execution doesn't even get to my map function, so I'm not sure what I'm doing wrong.

Thanks.

wonder95
  • 3,825
  • 8
  • 45
  • 74
  • 1
    `_.groupBy` followed by `_.map` is the correct solution. Can you explain what you mean by "doesn't even get to my map function"? It works in [this answer](http://stackoverflow.com/a/38774930/5743988), [this answer](http://stackoverflow.com/a/38485762/5743988), and [this answer](http://stackoverflow.com/a/38450804/5743988). – 4castle May 02 '17 at 00:26
  • @4castle I just mean that when I put a breakpoint inside the map() function in my debugger, it didn't stop there. I know it works, so I was assuming I was doing something wrong. – wonder95 May 02 '17 at 00:56

4 Answers4

5

_.groupBy chained with _.map is the simplest solution. A proper callback function for the _.map would be:

function (items) {
  items[0].count = items.length;
  return items[0];
}

Or you could condense it further with ES6 arrow-functions.

const myArray = [{"item":"Item 1","material":"Material1","type":"head"},{"item":"Item 1","material":"Material1","type":"head"},{"item":"Item 2","material":"Material2","type":"shell"},{"item":"Item 1","material":"Material1","type":"head"},{"item":"Item 2","material":"Material2","type":"shell"},{"item":"Item 3","material":"Material3","type":"support"},{"item":"Item 1","material":"Material1","type":"head"},{"item":"Item 3","material":"Material3","type":"support"},{"item":"Item 2","material":"Material2","type":"shell"}];

const myResultArray =
  _(myArray)
    .groupBy('item')
    .map(items => (items[0].count = items.length, items[0]))
    .value();

console.log(myResultArray);
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
4castle
  • 32,613
  • 11
  • 69
  • 106
1

If you want to do it without using Lodash, this would create the required array:

const res = myArray.reduce((accum, val) => {
  let summary = accum.get(val.item) || Object.assign({}, val, {count:0});
  summary.count++;
  return accum.set(val.item, summary);
}, new Map());

console.log([...res.values()]);
James Skemp
  • 8,018
  • 9
  • 64
  • 107
dashton
  • 2,684
  • 1
  • 18
  • 15
1

I ended up going with this:

var groupedData = [];
_(typeData).groupBy('item').forEach(function(value, key) {
  var obj = {};  
  obj.count = value.length;
  obj.type = value[0].type;
  obj.details = value[0].details;
  obj.description = key;
  groupedData.push(obj);
});                 
wonder95
  • 3,825
  • 8
  • 45
  • 74
0

using _.countBy and _.uniqBy

var res = _.chain(myArray)
    .countBy('item') // get each item count
    .thru(counts => // counts = {Item 1: 4, Item 2: 3, Item 3: 2}
        _.chain(myArray)
            .uniqBy('item') // get uniq items
            .map(item =>  // set each item count
                _.assign(
                    item, 
                    {count: counts[item.item]}
                );
            )
            .value();
    )
    .value();
stasovlas
  • 7,136
  • 2
  • 28
  • 29