thalesGog
thalesGog

Reputation: 351

How to count occurrences of object by property value, and store count with objects in new array

I want to count how many times the same value has occurred inside an object, and create a new object with the quantity added.

I have tried using filter, map and reduce but it didn't work.

I have this data:

let arrayOfObjects = [
    {name: 'Disney', type: 'inteira'},
    {name: 'Bottieli', type: 'inteira'},
    {name: 'Monster Truck', type: 'inteira'},
    {name: 'Xuxa', type: 'desconto'},
    {name: 'Pokémon', type: 'zaffari'},
]

And I want something like this output (make a new object without the repeated items based on the 'type' key value and showing the quantity of each item):

newArrayOfObjects = [
    {name: 'Disney', type: 'inteira', quantity: 3},
    {name: 'Xuxa', type: 'desconto', quantity: 1},
    {name: 'Pokémon', type: 'zaffari', quantity: 1}
]

Upvotes: 2

Views: 1832

Answers (1)

Dacre Denny
Dacre Denny

Reputation: 30360

There are a number of ways this can be achieved. One approach would be construct a mapping via the Array#reduce method, which maps each type to the corresponding item with count data included by the following (note that the use of a mapping like this is an optimisation):

  • iterate your input array
  • for each iteration, reduce the input to an mapping where the key of the map is the item type, and the value is the item (with count)
  • if a value for type key is found in the mapping, increment the count of the matching item
  • if a value for type key is not found in the mapping, insert a clone of the current item being iterated in reduce(), with an initial count of 1 included for that item
  • Pass the mapping created by reduce() to Object.values() to extract a flat Array of the items with corresponding counts computed during the reduction

Here's a working snippet to show this in action:

let arrayOfObjects = [
    {name: 'Disney', type: 'inteira'},
    {name: 'Bottieli', type: 'inteira'},
    {name: 'Monster Truck', type: 'inteira'},
    {name: 'Xuxa', type: 'desconto'},
    {name: 'Pokémon', type: 'zaffari'},
]

/* Iterate arrayOfObjects and reduce() this to a temporary mapping where item counts
are aggregated. Once that mapping is built, we'll extract values of the mapping to
get the desired array result (ie with items, and type counts) */
let newArrayOfObjects = Object.values(arrayOfObjects.reduce((mapping, item) => {
  
  /* Find exsiting item with matching item type in our mapping */
  const { [item.type]:matchingItem } = mapping;
  
  /* If matching item found, increment the count */
  if(matchingItem) {
    matchingItem.count ++;
  }
  /* Otherwise, insert item into mapping, and also include a starting count of one for it */
  else {
    mapping[ item.type ] = { ...item, count : 1 };
  }
  
  /* Return the updated mapping */
  return mapping;

},{}))

console.log(newArrayOfObjects);

Hope that helps :-)

Upvotes: 1

Related Questions