JavaScript recursive function loses return value

I want to find a string in a nested JSON object. If the string found in the object, I need to return this object.

I use a recursive function to achieve this. The problem is that the function recurses to the end and does not return the found object.

See all the code in jsfiddle

function search(obj, name) { console.log(obj["name"], ",", name, obj["name"] == name); if (obj["name"] == name) { return obj; //NOT RETURNING HERE } if (obj.children || obj._children) { var ch = obj.children || obj._children; //console.log(ch); ch.forEach(function(val) { search(val, name) }); } return -1; } search(myJson, "VM10-Proc4") 

I'm not sure what is going wrong.

+4
source share
4 answers

The correct return value is lost in the chain of recursive function calls. After the correct value is found, any additional search queries will return incorrect values ​​from this point.

A couple of ways to handle this:

1. Cancel the search

When the correct value is found, immediately return it all the way up the recursive stack, no longer looking for any current arrays or nested arrays. In other words, cancel the rest of the search.

@Barmer is an example of this. A key part of his code is the use of for loops, not the each method to iterate over arrays, since it is much easier to break the for loop.

2. Store the value somewhere safe

When the correct value is found, keep it somewhere safe, allow the search to continue, and after the initial function call completes access to the value. The easiest way is to store the correct value in a global variable, although this is not good practice as it violates the encapsulation of the function.

@shyam answer is a cleaner solution: passing a reference to a global variable as a parameter to the function, setting the parameter to the correct value, and then accessing the global variable after completing the initial function call.

Choice between two

Under the conditions of the laity, the alleged logic of the function can be summarized as follows: when you find what you are looking for, stop and let me know that it is right away. The only reason for continuing the search would be the presence of several pieces of data. I assume that is not the case here.

Of the two approaches, # 2 is a quick-fix workaround that should work fine but will further confuse anyone who trying to understand the intended logic of the function. Why is the search continuing if it only looking for a single piece of data that already been found?

# 1 is a refactoring of the function so that it behaves more consistently with the intended logic, which would make the function easier to understand. The function stops searching when it finds what it needs.

+4
source

You need to stop the cycle over the children when it finds one that matches.

 function search(obj, name) { console.log(obj.name, ",", name, obj.name == name); if (obj.name == name) { return obj; } if (obj.children || obj._children) { var ch = obj.children || obj._children; for (var i = 0; i < ch.length; i++) { var found = search(ch[i], name); if (found) { return found; } } } return false; } 

FIDDLE demo

+8
source

As you return, the return may be too deep for you to get a meaningful result. Instead, you can try passing an additional argument to collect the results.

 function search(obj, name, ret) { console.log(obj["name"], ",", name, obj["name"] == name); if (obj["name"] == name) { ret.push(obj); return } if (obj.children || obj._children) { var ch = obj.children || obj._children; ch.forEach(function(val) { search(val, name, ret); }); } } var result = []; search(myJson, "VM10-Proc4", result) 
+1
source

I know this is an old post, but I see 2 problems with this:

1) a recursive call does not return the result to the call stack, i.e.

 search(val, name) 

Must be

 return search(val, name) 

2) it looks like you are using array.forEach () . The documentation states:

It is not possible to stop or break the forEach () loop except to throw an exception. If you need this behavior, the forEach () method is the wrong tool. Use a simple loop instead. If you are testing array elements for a predicate and need a Boolean return value, you can use each () or some (). If available, the new find () or findIndex () methods can be used for early termination and for true predicates.

This means that when you find the result you are looking for, you want to send it back to the call stack. Using array.forEach will continue to recursively scan the hierarchy and return all values, not just those that interest you. Therefore, the last return value, perhaps you do not expect, for example, undefined! Therefore, use a different iteration method, i.e.

  for (var i = 0; i < ch.length; i++) { var val = ch[i]; return search(val, name, ret); } 

In the accepted answer, the lie gives you part of this answer, but does not explain why. Therefore this answer

0
source

All Articles