Let say i have a object response like this
const response = [
{
"title": "Menu 1",
"subMenu": [
{
"title": "Menu 1.2"
}
]
},
{
"title": "Menu 2",
},
{
"title": "Menu 3",
"subMenu": [
{
"title": "Menu 3.1",
"subMenu": [
{
"title": "Menu 3.2"
}
]
}
]
},
]
I want to get the object have title "Menu 3.1" using recursion so i wrote this function
const findElement = (arr, title) => {
for (let index = 0; index < arr.length; index ) {
const menu = arr[index];
if (menu.title === title) {
return menu;
} else if (menu.subMenu) {
return findElement(menu.subMenu, title);
}
}
};
and call it like this
console.log(findElement(response, "Menu 3.1" ))
but it log 'undefined'? What did i do wrong?
CodePudding user response:
The issue is, that the code checks response[0], it isn't a match, so checks responsep[0].submenu, but it returns the result of checking that, therefore, the for loop is short-circuited because the target isn't found
What you want to do, when checking sub-menu is check if the there is a result and only return it if there is
A little like this
const response = [{
"title": "Menu 1",
"subMenu": [{
"title": "Menu 1.2"
}
]
}, {
"title": "Menu 2",
}, {
"title": "Menu 3",
"subMenu": [{
"title": "Menu 3.1",
"subMenu": [{
"title": "Menu 3.2"
}
]
}
]
},
]
const findElement = (arr, title) => {
for (let index = 0; index < arr.length; index ) {
const menu = arr[index];
if (menu.title === title) {
return menu;
} // no need for else since we return above
if (menu.subMenu) {
const sub = findElement(menu.subMenu, title);
if (sub) return sub;
}
}
};
console.log(findElement(response, "Menu 3.1"))
CodePudding user response:
I'd definitely use a reducer here:
const reducer = (result, item) =>
result ||
(item.title === "Menu 3.1"
? item
: item.subMenu?.reduce(reducer, null) || null)
console.log(response.reduce(reducer, null))
const response = [
{
title: "Menu 1",
subMenu: [
{
title: "Menu 1.2"
}
]
},
{
title: "Menu 2"
},
{
title: "Menu 3",
subMenu: [
{
title: "Menu 3.1",
subMenu: [
{
title: "Menu 3.2"
}
]
}
]
}
]
const findItemByTitle = (items, title) => {
const reducer = (a, item) =>
a ||
(item.title === title ? item : item.subMenu?.reduce(reducer, null) || null)
return items.reduce(reducer, null)
}
console.log(findItemByTitle(response, "Menu 3.1"))
I'd argue it's easy to follow:
- if there's already a result, return it
- if not, return current item if the title is what we're looking for
- if not and there's a
subMenu
return reducer's result on the submenu - else return
null
(which goes to next item)