递归JavaScript函数正在丢失返回值

时间:2013-03-28 05:02:50

标签: javascript json recursion nested

我想在嵌套的JSON对象中搜索字符串。如果在对象中找到字符串,我需要返回该对象。

我正在使用递归函数来实现这一目标。问题是,函数递归到最后并且没有返回找到的对象。

Please see the entire 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")

我不确定会出现什么问题。

4 个答案:

答案 0 :(得分:8)

当找到匹配的子项时,您需要停止循环子项。

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

答案 1 :(得分:4)

正确的返回值在递归函数调用链中丢失。找到正确的值后,所做的任何其他搜索都将从该点返回不正确的值。

有几种方法可以解决这个问题:

<强> 1。取消搜索

当找到正确的值时,立即将它一直返回到递归堆栈,而不再搜索当前数组或嵌套数组。换句话说,取消搜索的其余部分。

@ Barmer的答案就是一个例子。他的代码的关键部分是使用for循环而不是each方法来遍历数组,因为中断for循环要容易得多。

<强> 2。将值存储在安全的地方

找到正确的值后,将其存储在安全的地方,允许其余的搜索继续,并在初始函数调用完成后访问该值。最简单的方法是将正确的值存储在全局变量中,尽管这不是一个好的做法,因为它违反了函数的封装。

@ shyam的答案提供了一个更清晰的解决方案:将对全局变量的引用作为函数参数传递,在找到正确的值时设置参数,然后在初始函数调用完成后访问全局变量。

在两者之间进行选择

用外行人的话说,这个功能的预期逻辑可以总结如下:当你找到你想要的东西时,停下来,让我立刻知道它是什么。继续搜索的唯一原因是需要找到多个数据。我假设这不是这种情况。

在这两种方法中,#2是一种快速解决方法,应该可以正常工作,但会进一步混淆任何试图理解该功能的预期逻辑的人。如果只搜索已经找到的单个数据,为什么搜索仍在继续?

#1是函数的重构,因此它的行为与预期的逻辑更加一致,这将使函数更容易理解。该功能在找到所需内容时停止搜索。

答案 2 :(得分:1)

由于您正在递归,因此返回可能嵌套得太深,无法获得有意义的结果。相反,您可以尝试传递额外的参数来收集结果。

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)

答案 3 :(得分:0)

我知道这是一篇旧帖子,但我可以看到2个问题:

1)递归调用不会将结果返回到调用堆栈,即

search(val, name)

应该是

return search(val, name)

2)看起来你正在使用array.forEach()。文档说明:

  

除了抛出异常之外,没有办法停止或中断forEach()循环。如果您需要这样的行为,forEach()方法是错误的工具。请改用普通循环。如果要测试谓词的数组元素并需要布尔返回值,则可以使用every()或some()。如果可用,新方法find()或findIndex()也可用于在真正的谓词上提前终止。

这意味着,实际上,当您找到要查找的结果时,您希望将其发送回调用堆栈。使用array.forEach将继续以递归方式查看层次结构并返回所有值,而不仅仅是您感兴趣的值。因此,最后返回的值可能是您不期望的值,例如undefined!因此,使用不同的迭代方法,即

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

接受的答案是勺子喂你这个答案的一部分,但没有解释原因。因此这个答案