2

I'm using ReactJs where I need to search in array of objects

Here is array

const menuList = [
  {
    title: "Top 1",
    link: "top1",
  },
  {
    title: "Top 2",
    link: "top2",
    children: [
      {
        title: "Top 2",
        link: "top2",
        parent: true
      },
      {
        title: "Top 2 child 1",
        link: "top2-child-1",
      },
      {
        title: "Top 2 children 2",
        link: "top2-child-2",
        children: [
          {
            title: "deep nested child",
            link: "deep-nested-child"
          }
        ]
      },
      {
        title: "Top 2 child 2",
        link:"top2-child-2"
      },
      {
        title: "Top 2 child 3",
        link: "top2-child-3",
        children: [
          {
            title: "Nested child of Child 3",
            link: "nested-child-of-3"
          }
        ]
      }
    ]
  }
];

I want to search based on link But I want to search first in deep children then the top one. Search start from the deepest children and go to top until it find the match. If no match found it will return the same menuList back. If any match found then I want to get back whole array of this level not the single object.

For example if I search top2-child-1 then it should return.

     [
      {
        title: "Top 2",
        link: "top2",
        parent: true
      },
      {
        title: "Top 2 child 1",
        link: "top2-child-1",
      },
      {
        title: "Top 2 children 2",
        link: "top2-child-2",
        children: [
          {
            title: "deep nested child",
            link: "deep-nested-child"
          }
        ]
      },
      {
        title: "Top 2 child 2",
        link:"top2-child-2"
      },
      {
        title: "Top 2 child 3",
        link: "top2-child-3",
        children: [
          {
            title: "Nested child of Child 3",
            link: "nested-child-of-3"
          }
        ]
      }
    ]

What I'm trying is here.

const find_nested_link = (menuList, path) => {
  for (let i = 0; i < menuList.length; i++) {
    if (menuList[i].children) {
      return find_nested_link(menuList[i].children, path);
    } else {
      if (menuList[i].link === path) {
        return menuList;
      }
    }
  }
};

It will work only for first deep nested child. if you search nested-child-of-3 it will not able to find it.

Here is another one which is also not work perfect.

const result = menuList.reduce((r, { children }) => {
  let o = children.filter(({ link }) => link === path);
  if (o && o.length) r.push(...children);
  return r;
}, []);
3
  • Sounds like a solution requring a recursive method Commented Aug 23, 2020 at 5:19
  • But the above recursive method is not working perfectly. Can you please look at this Commented Aug 23, 2020 at 5:26
  • I have provided two methods. I see you wanted the original array back from the method if nothing was found. You can easily do a check on the search result, and use the original if the result array is empty. Commented Aug 23, 2020 at 6:28

3 Answers 3

4

I have provided two methods below:

  1. Search for first match in the tree, return if found and containing array.
  2. Search for all matches, returns containing array, object it was found in, and depth it was found at

Method 1: CodeSandbox

Here is a recursive method that doesnt use reduce. Simple type checking to search for a provided key value, and will search through any child arrays, with names that do not have to be "children". It provides a "found" result as well as an array in which it was found. You could extend this to return the object in which it was found too, quite easily.

const recursivelyFindKeyValue= (key, keyValue, list) => {
  console.log("Searching list: ", list);

  for (let i = 0; i < list.length; i++) {
    const item = list[i];

    for (const key of Object.keys(item)) {
      //check if its array of more options, search it
      if (Array.isArray(item[key])) {
        console.log("child array found, searching", item);
        const res = recursivelyFindKeyValue(key, keyValue, item[key]);
        if (res.found === true) return res;
      }
      //Test the keyValue
      else if (item[key] === keyValue) {
        //found, return the list
        console.log("found ", keyValue);
        return { found: true, containingArray: list };
      }
    }
  }

  return { found: false, containingArray: [] };
};

usage

const res = recursivelyFindKeyValue("link", "top2-child-2", menuList);
const res = recursivelyFindKeyValue("link", "nested-child-of-3", menuList);

Method 2: CodeSandbox

This method will search the entire tree, return all results with their parent array, object which they key:value was found, as well as the depth at which it was found.

const recursivelyFindKeyValue = (key, keyValue, list, depth = 0) => {
  console.log("Searching list: ", list);
  let results = [];

  for (let i = 0; i < list.length; i++) {
    const item = list[i];

    for (const key of Object.keys(item)) {
      //check if its not an array
      if (Array.isArray(item[key])) {
        console.log("child array found, searching", item);
        let res = recursivelyFindKeyValue(key, keyValue, item[key], depth + 1);
        results = results.concat(res);
      }
      //we have found it
      else if (item[key] === keyValue) {
        //found, return the list
        console.log("found ", keyValue);
        results.push({
          found: true,
          containingArray: list,
          depth: depth,
          object: item
        });
      }
    }
  }

  return results;
};

I created another link "top2-child-2" in the codesandbox example deeper down so you can see the result of multiple depths.

Sign up to request clarification or add additional context in comments.

Comments

0

Can you try this recursion?

const find_nested_link = (menuList, path) => (
  menuList.reduce((found, item) => {
    if(found) return found
    if(item.children && item.children.length > 0) {
        return find_nested_link(item.children, path)
    }
    return item.link == path ? item : null
  }, null)
)

https://jsfiddle.net/ndqu6a4L/

Comments

0

I believe the above answer returns the object the matching link belongs to, and you want the children. Try this.

function recursiveSearch( children, query ) {
    //the last thing to return from this function
    toreturn = []
    
    for( let i =0; i < children.length; i++ ) {
        //console.log( children[i].link  + " == " + query )
        if( children[i].link === query ) {

            toreturn = children.slice() 
            
            return children.slice()
        } else {
            if( children[i].hasOwnProperty( "children" )) {
                let subchildren = recursiveSearch( children[i].children, query )
                //console.log( subchildren )
                if( subchildren.length > 0 ) {
                    toreturn = subchildren.slice()
                }
            }
        }
    }

    return toreturn

}



children = recursiveSearch( menuList, "top2-child-2" )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.