Reputation: 61
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
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
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
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
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