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.

2
  • 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, this answer, and this answer. Commented May 2, 2017 at 0: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. Commented May 2, 2017 at 0:56

4 Answers 4

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>

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

Comments

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);
});                 

4 Comments

Will you please explain what is _(typeData) here ?
typeData is the name I gave to the original array of source data (listed at the beginning of the question).
So for implementing this am I need to add underscore js ?
No, it uses Lodash. However, other answers above use pure JS without Lodash, so you could try those options, too.
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()]);

Comments

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();

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.