Question:
How to get all the objects from an array that is direct/nested children of another object in JS

Problem

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 which 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 are direct or nested children of beauty (id:1 ). So the new array should look like this. Please take into consideration that there may exist more nested categories.


const newCategoriesArray  = [

    {id:3, name:'hair care', parentCategoryId:1},

    {id:4, name:'hair oil', parentCategoryId:3},

    {id:5, name:'kumarika hair oil', parentCategoryId:4},

]


Solution

You have some interesting options already, provided categories aren'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):


const categoriesById = new Map(

    categories.map((category) => [category.id, category])

);


Then get the target category ID (since you seem to be starting with a category object) and an array for the results:


const targetId = category.id;

const results = [];


Then loop through the categories:


for (const cat of categories) {


In the loop, we'll go through the cat's ancestors (if any) to see if any of them match the target:

  

// Start with this category in `c`...

    let c = cat;

    // While `c` exists and has a parent...

    while (c && c.parentCategoryId) {

        // Get its parent

        c = c.parentCategoryId && categoriesById.get(c.parentCategoryId);

        // Does the parent match?

        if (c.id === targetId) {

            // Found it

            results.push(cat);

            break;

        }

        // The loop will continue searching ancestry of `cat` via `c`

    }


and that's it!


Working example (rather shorter without the explanatory comments):



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 }, ];

const categoriesById = new Map( categories.map((category) => [category.id, category]));


const getAllChildCategories = (category) => {

    const targetId = category.id;

    const results = [];

    for (const cat of categories) {

        let c = cat;

        while (c && c.parentCategoryId) {

            c = c.parentCategoryId && categoriesById.get(c.parentCategoryId);

            if (c.id === targetId) {

                results.push(cat);

                break;

            }

        }

    }

    return results;

};



console.log(getAllChildCategories(categories[0]));

.as-console-wrapper {

    max-height: 100% !important;

}


Suggested blogs:

>How to use querySelectorAll()" with multiple conditions in JavaScript?

>How to fix mouseover event glitch in JavaScript?

>How to do light and dark mode in a website using HTML and JavaScript?

>How to manipulate manipulating Array object in JavaScript?

>How to merge an object into Array with the same key in JavaScript?

>Javascript Error Solved: Property 'id' does not exist on type 'T'

>Why highlighted table row using class not working in JavaScript?

>How to rename an object key based on the condition in JavaScript?

>How to sort an array based on another array in Javascript?

>Javascript: Modal not closing with a button


Nisha Patel

Nisha Patel

Submit
0 Answers