I was just practicing some native Javascript and came across this problem. I'm building a comments widget and trying to implement a 'reply' button. For this I have to iterate through some n number of nested comments to find the correct one and push the reply to it's 'responses' attribute. This is my code so far:
const recursiveSearch = (object, target) => {
if(object.id === target) return object;
let result;
if(object.responses.length > 0) {
object.responses.forEach(response => {
if(response.id === target) {
result = response;
console.log('match found')
console.log(response)
return response
}
else if(response.responses.length > 0) recursiveSearch(response, target)
})
};
console.log('result Is')
console.log(result)
return result
}
The logs show the expected behavior just fine but when looking at the end return statement is undefined. Any way to get around this?
You can dramatically simplify your program using generators and reusable functions -
function first(it) {
for (const v of it)
return v
}
function *search(t, id) {
if (t.id == id) yield t
for (const r of t.responses) yield *search(r, id)
}
const mydata =
{ id: 1, responses: [
{ id: 2, responses: [] },
{ id: 3, responses: [] },
{ id: 4, responses: [
{ id: 5, responses: [] },
{ id: 6, responses: [] },
{ id: 7, responses: [] }
]},
{ id: 8, responses: [
{ id: 9, responses: [
{ id: 10, responses: [] }
]}
]}
]}
console.log(first(search(mydata, 8))) // { id: 8, responses: [ ... ] }
console.log(first(search(mydata, 100))) // undefined
Even better is the choice to make search generic, accepting a match and next function. Now you can search input data of any type or shape with any fields. Ie, you're not limited to just id and responses -
function first(it) {
for (const v of it)
return v
}
function *search(t, match, next) {
if (Boolean(match(t))) yield t
for (const r of next(t) ?? []) yield *search(r, match, next)
}
function mysearch(t, id) {
return search(t, t => t.id == id, t => t.responses)
}
const mydata =
{ id: 1, responses: [
{ id: 2, responses: [] },
{ id: 3, responses: [] },
{ id: 4, responses: [
{ id: 5, responses: [] },
{ id: 6, responses: [] },
{ id: 7, responses: [] }
]},
{ id: 8, responses: [
{ id: 9, responses: [
{ id: 10, responses: [] }
]}
]}
]}
console.log(first(mysearch(mydata, 8))) // { id: 8, responses: [ ... ] }
console.log(first(mysearch(mydata, 100))) // undefined
You forgot to return in your else if, but notice, you're inside a forEach, so maybe change it to regular for or use something else
You should return from the recursive call and assign to the result variable again.
const recursiveSearch = (object, target) => {
if (object.id === target) return object;
let result;
if (object.responses.length > 0) {
object.responses.forEach(response => {
if (response.id === target) {
result = response;
console.log('match found')
console.log(response)
return response
}
else if (response.responses.length > 0) {
result = recursiveSearch(response, target) // <--------------------
}
})
};
console.log('result Is')
console.log(result)
return result
}
You can also use a find instead of forEach. This is more efficient.
const recursiveSearch = (object, target) => {
if (object.id === target) return object;
const result = object.responses.find(response => {
if (response.id === target) {
console.log('match found')
console.log(response)
return response
}
else if (response.responses.length > 0) {
return recursiveSearch(response, target)
}
})
console.log('result Is')
console.log(result)
return result
}