John107
John107

Reputation: 2287

Using Lodash countBy for nested objects

I have some json, example below:

[
 {
  "category": {
   "name": "Technology",
   "key": "012"
  }
 },
 {
  "category": {
   "name": "Kitchen",
   "key": "016"
  }
 },
 {
  "category": {
   "name": "Technology",
   "key": "012"
  }
 }
]

I want to count the occurrences from the data and map them to create a list in a react component. I want to display, the name, key and occurrences they appear in the list. And I want to display only 1 instance of the item with its occurrences in the list. I want to use Lodash to achieve this.

// Example 1 - Lists each category with the correct values. eg. Technology(012,2), Kitchen(016,1), Technology(012,2).
const categories = items.map( item => item.category )
const countCategories = countBy(categories)

// React Component
{ Object.keys(categories).map(category =>
 <CustomComponent category={category.name} key={category.key} count={countCategories[category.name]} />
// Example 2 - Lists only one instance of each unique category by name with the count but not the key. eg. Technology(,2), Kitchen(,1)
const categories = items.map( item => item.category.name )
const countCategories = countBy(categories)

// React Component
{ Object.keys(countCategories).map(category =>
 <CustomComponent category={category} key={'NA'} count={countCategories[category]} />
)}

The desired outcome I'm looking for, is to list only one instance of each category in the list, with the name, count and key. It seems I can only count the occurrences of .name or .key which in turn means I can't include the other in my map. How can I achieve this using Lodash?

Upvotes: 2

Views: 877

Answers (2)

Ori Drori
Ori Drori

Reputation: 191976

With lodash you can use _.groupBy() to batch items by category.name, and then map the groups, and create an array of categories with the count:

const arr = [
 { "category": { "name": "Technology", "key": "012" } },
 { "category": { "name": "Kitchen", "key": "016" } },
 { "category": { "name": "Technology", "key": "012" } }
];

const result = _.map(
  _.groupBy(arr, 'category.name'),
 items => ({
  ...items[0].category,
  count: items.length
 })
)

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Upvotes: 1

Majed Badawi
Majed Badawi

Reputation: 28414

Using Array#reduce, iterate over the list while updating a Map where the key is the category name and the value is the category with the updated count.

The result would be the final values of the Map which are the categories grouped by the name, each with the final count:

const arr = [
 { "category": { "name": "Technology", "key": "012" } },
 { "category": { "name": "Kitchen", "key": "016" } },
 { "category": { "name": "Technology", "key": "012" } }
];

const res = [...
  arr.reduce((categoryMap, { category }) => {
    if(categoryMap.has(category.name)) {
      categoryMap.get(category.name).count++;
    } else {
      categoryMap.set(category.name, {...category, count: 1});
    }
    return categoryMap;
  }, new Map)
  .values()
];

console.log(res);

Upvotes: 2

Related Questions