StaceyW
StaceyW

Reputation: 61

Count Unique Value from Two object of array in Javascript

Given an array of categories and an array of entries, creates an array of objects with a category name and an entry count. Consider id is equal to categoryId.

var categories = [
  { name: 'Cats', id: 10 },
  { name: 'Dogs', id: 20 },
 
];

var entries = [
  {categoryId: 10, name: 'Fluffy'},
  {categoryId: 10, name: 'Spot'},
  {categoryId: 10, name: 'Lil'},
  {categoryId: 20, name: 'Tom'},
  {categoryId: 20, name: 'Buck'},
  {categoryId: 20, name: 'Flo'},
  {categoryId: 20, name: 'Cheek'},
  {categoryId: 10, name: 'Stan'},
  {categoryId: 20, name: 'Stila'}
  
]

Expected Output: [{ name:'Cats', count: 4 }, { name:'Dogs', count: 5 }];

I wrote it like this below, but there seem to be a performance problem when you try to run it through hundreds of categories and tens of thousands of entries.

const categoriesByEntryCount = (categories, entries) =>
  categories.map(category => ({
    name: category.name,
    count: entries.filter(entry => entry.categoryId === category.id).length,
  }));

My question is there another way to write or implement this ?

Upvotes: 3

Views: 97

Answers (4)

Redu
Redu

Reputation: 26161

This is obviously a reducing job.

var categories = [ { name: 'Cats', id: 10 }
                 , { name: 'Dogs', id: 20 }
                 ],
    entries    = [ {categoryId: 10, name: 'Fluffy'}
                 , {categoryId: 10, name: 'Spot'}
                 , {categoryId: 10, name: 'Lil'}
                 , {categoryId: 20, name: 'Tom'}
                 , {categoryId: 20, name: 'Buck'}
                 , {categoryId: 20, name: 'Flo'}
                 , {categoryId: 20, name: 'Cheek'}
                 , {categoryId: 10, name: 'Stan'}
                 , {categoryId: 20, name: 'Stila'}
                 ],
    result     = entries.reduce((cs,e) => ( cs.map(c => c.id === e.categoryId ? c.count ? c.count++
                                                                                        : c.count = 1
                                                                              : c)
                                          , cs
                                          ), categories);

console.log(result);

You may complain that the result includes the id property but that's just good.

Upvotes: 0

Ravikumar
Ravikumar

Reputation: 2205

We can do like below with time complexity O(M + N)

var categories = [
  { name: 'Cats', id: 10 },
  { name: 'Dogs', id: 20 },
 
];

var entries = [
  {categoryId: 10, name: 'Fluffy'},
  {categoryId: 10, name: 'Spot'},
  {categoryId: 10, name: 'Lil'},
  {categoryId: 20, name: 'Tom'},
  {categoryId: 20, name: 'Buck'},
  {categoryId: 20, name: 'Flo'},
  {categoryId: 20, name: 'Cheek'},
  {categoryId: 10, name: 'Stan'},
  {categoryId: 20, name: 'Stila'}
  
]

const categoriesByEntryCount = (categories, entries) => {

    const entriesHash = entries.reduce((acc, ele) => {
      acc[ele.categoryId] =  acc[ele.categoryId] ? acc[ele.categoryId] + 1 : 1;
      return acc;
    }, {});
    
  return categories.map(category => ({
    name: category.name,
    count: entriesHash[category.id],
  }));
 }
 
 console.log(categoriesByEntryCount(categories, entries))

Upvotes: 0

Som Shekhar Mukherjee
Som Shekhar Mukherjee

Reputation: 8168

You need to use Maps in all possible places.

var categories = new Map();
categories.set(10, 'Cats');
categories.set(20, 'Dogs');

var entries = [
  { categoryId: 10, name: 'Fluffy' },
  { categoryId: 10, name: 'Spot' },
  { categoryId: 10, name: 'Lil' },
  { categoryId: 20, name: 'Tom' },
  { categoryId: 20, name: 'Buck' },
  { categoryId: 20, name: 'Flo' },
  { categoryId: 20, name: 'Cheek' },
  { categoryId: 10, name: 'Stan' },
  { categoryId: 20, name: 'Stila' },
];

console.log(Array.from(
  entries.reduce(
    (m, { categoryId, name }) =>
      m.set(categoryId, (m.get(categoryId) || 1) + 1),
    new Map()
  ),
  ([k, v]) => ({ name: categories.get(k), count: v })
));

Upvotes: 1

Majed Badawi
Majed Badawi

Reputation: 28414

const categories = [ { name: 'Cats', id: 10 }, { name: 'Dogs', id: 20 } ];
const entries = [ { categoryId: 10, name: 'Fluffy' }, { categoryId: 10, name: 'Spot' }, { categoryId: 10, name: 'Lil' }, { categoryId: 20, name: 'Tom' }, { categoryId: 20, name: 'Buck' }, { categoryId: 20, name: 'Flo' }, { categoryId: 20, name: 'Cheek' }, { categoryId: 10, name: 'Stan' }, { categoryId: 20, name: 'Stila' } ];

// get number of occurences of each category in entries
const categoriesCount = entries.reduce((countMap, { categoryId }) => 
  countMap.set( categoryId, 1 + (countMap.get(categoryId) || 0) )
, new Map);

// iterate over categories and return name and count in categoriesCount
const res = categories.map(({ name, id }) => 
  ({ name, count: categoriesCount.get(id) })
);

console.log(res);

Upvotes: 0

Related Questions