So I have a javascript array that looks like this (simplified).
Here beauty (id:1
) and health (id:2
) is root categories as they have a null value of parentCategoryId
.
hair care (id:3
), hair oil (id:4
) and kumarika hair oil (id:5
) falls into the beauty category(id:1
) as they have parentCategoryId
value which directly or indirectly falls into beauty category(see below for explaination).But supplements (id:6
) falls into the health category (id:2
).
How do I get all the categories that (directly or nested) falls into the beauty category (id:1
).
key parentCategoryId
is the identifier of which category directly falls into which category.
so here
id:3
has parentCategoryId:1
that means id:3
is direct children of id:1
.So falls into beauty category.
id:4
has parentCategoryId:3
that means id:4
is direct children of id:3
which is direct children of id:1
.so falls into beauty category.
id:5
has parentCategoryId:4
that means id:5
is direct children of id:4
which is direct children of id:3
which is direct children of id:1
and falls into beauty category.
const categories = [
{id:1,name:'beauty',parentCategoryId:null},
{id:2, name:'health', parentCategoryId:null},
{id:3, name:'hair care', parentCategoryId:1},
{id:4, name:'hair oil', parentCategoryId:3},
{id:5, name:'kumarika hair oil', parentCategoryId:4},
{id:6, name:'supplements', parentCategoryId:2}
]
I have tried recursive function but couldn’t find the appropriate output.Say for example I am calling this recursive function with the object that has id:1
.
let newCategoriesArray =[]
const getAllChildCategories = (category) => {
let childCategories = categories.filter(
(cat) => cat.parentCategory == category.id
);
newCategoriesArray.push(...childCategories);
if (childCategories.length > 0) {
childCategories.map((cat) => {
getAllChildCategories(cat);
});
}
};
My expected output should be an array of categories that is direct or nested children of beauty (id:1
).
So the new array should look like this.please take into consideration there may exist more nested categorie.
const newCategoriesArray = [
{id:3, name:'hair care', parentCategoryId:1},
{id:4, name:'hair oil', parentCategoryId:3},
{id:5, name:'kumarika hair oil', parentCategoryId:4},
]
4
Answers
filter()
was a good direction, just it can’t access its own results on the go, and that’s why the4
–5
pair becomes hard.reduce()
gives access to the result all the time, I’d suggest using that:The
(*)
part is whatfilter()
can’t do directly.This approach builds on that sub-categories always appear after their parent, so for example "kumarika hair oil" will never come before "hair oil" (and "beauty"). If that’s not true, it won’t work.
After seeing the comment here is a variant that does not rely on the order of categories-subcategories (input array has been rearranged). It preprocesses the original data into a
parentCategory
-indexed array of arrays, and then takes the result from that one with a very minimal recursive step, taking the preprocessed item directly (the...prep[id]
part) and then checking if those items have descendants (the part with theflatMap()
call):I think something similar may have appeared in one of the deleted answers, but it’s not visible now.
You have some interesting options already, provided
categories
isn’t a very long list (like thousands of entries). (They do lots of looping through the list.)If there are a lot of categories in the list (at least thousands), but the hierarchies aren’t hundreds deep, I’d probably just loop through
categories
once, checking the ancestry of each target category as I went.I’d start by keeping a map of categories by their ID (just after the
categories
array, unless that’s dynamic; if it’s dynamic, create the map on the fly):Then get the target category ID (since you seem to be starting with a category object) and an array for the results:
Then loop through the categories:
In the loop, we’ll go through
cat
‘s ancestors (if any) seeing if any of them matches the target:and that’s it!
Working example (rather shorter without the explanatory comments):
A quite popular solution would be to build a parent id map with children for particular parent id and traverse this map (this is good when the order of the items is random), otherwise tevemadar’s solution is faster (but not very safe as T.J. Crowder mentioned).
This solution uses stack data structure to achieve this. I would consider this an optimal solution. The time complexity is O(n). I have left comments every step of the way to explain how I came up with it.