【问题标题】:Recursive JavaScript function is losing the return value递归 JavaScript 函数正在丢失返回值
【发布时间】:2013-03-18 11:08:03
【问题描述】:

我想在嵌套的 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")

我不知道出了什么问题。

【问题讨论】:

    标签: javascript json recursion nested


    【解决方案1】:

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

    几种处理方法:

    1.取消搜索

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

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

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

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

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

    在两者之间选择

    通俗地说,函数的预期逻辑可以概括如下:当你找到你要找的东西时,停下来,并立即告诉我它是什么。继续搜索的唯一原因是需要找到多条数据。我假设这里不是这种情况。

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

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

    【讨论】:

    • 谢谢马特!你能解释一下关于选项 1 的更多信息吗?
    • @Aneesh:我更新了答案,对这两种方法进行了更彻底的解释,哪一种可能更好。
    【解决方案2】:

    当找到匹配的子节点时,您需要停止循环遍历子节点。

    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 更改为false 很重要:)
    【解决方案3】:

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

    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)
    

    【讨论】:

    • 谢谢希亚姆!!但在这种情况下,该函数也会重复到最后。对吗?
    • 是的。如果你想立即停止,你必须使用循环而不是使用 forEach
    【解决方案4】:

    我知道这是一篇旧帖子,但我可以看到 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);
        }
    

    接受的答案确实会为您提供部分答案,但没有解释原因。因此这个答案

    【讨论】:

    • 那件事情让我的一天都丧命了。谢谢!
    【解决方案5】:

    这是使用object-scan的解决方案

    // const objectScan = require('object-scan');
    
    const myJson = {"name":"UCS - San Jose","type":"Minor","children":[{"name":"VM1","type":"Clear","children":[{"name":"VM1-Proc1","type":"Clear","children":[{"name":"VM1-Proc1-child1","type":"Clear"}]},{"name":"VM1-Proc2","type":"Clear"},{"name":"VM1-Proc3","type":"Clear"},{"name":"VM1-Proc4","type":"Clear"},{"name":"VM1-Proc5","type":"Clear"},{"name":"VM1-Proc6","type":"Clear"},{"name":"VM1-Proc7","type":"Clear"},{"name":"VM1-Proc8","type":"Clear"},{"name":"VM1-Proc9","type":"Clear"},{"name":"VM1-Proc10","type":"Clear"}]},{"name":"VM2","type":"Clear","children":[{"name":"VM2-Proc1","type":"Clear"},{"name":"VM2-Proc2","type":"Clear"},{"name":"VM2-Proc3","type":"Clear"},{"name":"VM2-Proc4","type":"Clear"},{"name":"VM2-Proc5","type":"Clear"},{"name":"VM2-Proc6","type":"Clear"},{"name":"VM2-Proc7","type":"Clear"},{"name":"VM2-Proc8","type":"Clear"},{"name":"VM2-Proc9","type":"Clear"},{"name":"VM2-Proc10","type":"Clear"}]},{"name":"VM3","type":"Clear","children":[{"name":"VM3-Proc1","type":"Clear"},{"name":"VM3-Proc2","type":"Clear"},{"name":"VM3-Proc3","type":"Clear"},{"name":"VM3-Proc4","type":"Clear"},{"name":"VM3-Proc5","type":"Clear"},{"name":"VM3-Proc6","type":"Clear"},{"name":"VM3-Proc7","type":"Clear"},{"name":"VM3-Proc8","type":"Clear"},{"name":"VM3-Proc9","type":"Clear"},{"name":"VM3-Proc10","type":"Clear"}]},{"name":"VM4","type":"Minor","children":[{"name":"VM4-Proc1","type":"Clear"},{"name":"VM4-Proc2","type":"Clear"},{"name":"VM4-Proc3","type":"Minor"},{"name":"VM4-Proc4","type":"Clear"},{"name":"VM4-Proc5","type":"Clear"},{"name":"VM4-Proc6","type":"Minor"},{"name":"VM4-Proc7","type":"Clear"},{"name":"VM4-Proc8","type":"Clear"},{"name":"VM4-Proc9","type":"Clear"},{"name":"VM4-Proc10","type":"Clear"}]},{"name":"VM5","type":"Clear","children":[{"name":"VM5-Proc1","type":"Clear"},{"name":"VM5-Proc2","type":"Clear"},{"name":"VM5-Proc3","type":"Clear"},{"name":"VM5-Proc4","type":"Clear"},{"name":"VM5-Proc5","type":"Clear"},{"name":"VM5-Proc6","type":"Clear"},{"name":"VM5-Proc7","type":"Clear"},{"name":"VM5-Proc8","type":"Clear"},{"name":"VM5-Proc9","type":"Clear"},{"name":"VM5-Proc10","type":"Clear"}]},{"name":"VM6","type":"Minor","children":[{"name":"VM6-Proc1","type":"Clear"},{"name":"VM6-Proc2","type":"Clear"},{"name":"VM6-Proc3","type":"Minor"},{"name":"VM6-Proc4","type":"Clear"},{"name":"VM6-Proc5","type":"Clear"},{"name":"VM6-Proc6","type":"Clear"},{"name":"VM6-Proc7","type":"Minor"},{"name":"VM6-Proc8","type":"Clear"},{"name":"VM6-Proc9","type":"Clear"},{"name":"VM6-Proc10","type":"Clear"}]},{"name":"VM7","type":"Clear","children":[{"name":"VM7-Proc1","type":"Clear"},{"name":"VM7-Proc2","type":"Clear"},{"name":"VM7-Proc3","type":"Clear"},{"name":"VM7-Proc4","type":"Clear"},{"name":"VM7-Proc5","type":"Clear"},{"name":"VM7-Proc6","type":"Clear"},{"name":"VM7-Proc7","type":"Clear"},{"name":"VM7-Proc8","type":"Clear"},{"name":"VM7-Proc9","type":"Clear"},{"name":"VM7-Proc10","type":"Clear"}]},{"name":"VM8","type":"Clear","children":[{"name":"VM8-Proc1","type":"Clear"},{"name":"VM8-Proc2","type":"Clear"},{"name":"VM8-Proc3","type":"Clear"},{"name":"VM8-Proc4","type":"Clear"},{"name":"VM8-Proc5","type":"Clear"},{"name":"VM8-Proc6","type":"Clear"},{"name":"VM8-Proc7","type":"Clear"},{"name":"VM8-Proc8","type":"Clear"},{"name":"VM8-Proc9","type":"Clear"},{"name":"VM8-Proc10","type":"Clear"}]},{"name":"VM9","type":"Clear","children":[{"name":"VM9-Proc1","type":"Clear"},{"name":"VM9-Proc2","type":"Clear"},{"name":"VM9-Proc3","type":"Clear"},{"name":"VM9-Proc4","type":"Clear"},{"name":"VM9-Proc5","type":"Clear"},{"name":"VM9-Proc6","type":"Clear"},{"name":"VM9-Proc7","type":"Clear"},{"name":"VM9-Proc8","type":"Clear"},{"name":"VM9-Proc9","type":"Clear"},{"name":"VM9-Proc10","type":"Clear"}]},{"name":"VM10","type":"Clear","children":[{"name":"VM10-Proc1","type":"Clear"},{"name":"VM10-Proc2","type":"Clear"},{"name":"VM10-Proc3","type":"Clear"},{"name":"VM10-Proc4","type":"Clear"},{"name":"VM10-Proc5","type":"Clear"},{"name":"VM10-Proc6","type":"Clear"},{"name":"VM10-Proc7","type":"Clear"},{"name":"VM10-Proc8","type":"Clear"},{"name":"VM10-Proc9","type":"Clear"},{"name":"VM10-Proc10","type":"Clear"}]}]};
    
    const search = (obj, name) => objectScan(['**.name'], {
      rtn: 'parent',
      abort: true,
      filterFn: ({ value }) => value === name
    })(obj);
    
    console.log(search(myJson, 'VM10-Proc4'));
    // => { name: 'VM10-Proc4', type: 'Clear' }
    .as-console-wrapper {max-height: 100% !important; top: 0}
    &lt;script src="https://bundle.run/object-scan@13.7.1"&gt;&lt;/script&gt;

    免责声明:我是object-scan的作者

    【讨论】:

      猜你喜欢
      • 2021-09-20
      • 2017-09-24
      • 2012-05-29
      • 1970-01-01
      • 2022-11-29
      • 2016-03-01
      • 1970-01-01
      • 2020-10-10
      相关资源
      最近更新 更多