递归的JavaScript函数丢失了返回值

6
我想在嵌套的JSON对象中搜索一个字符串。如果找到了该字符串,则需要返回该对象。
我正在使用递归函数来实现这一点。问题是,该函数一直递归到最后,但没有返回找到的对象。 请在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")

我不确定出了什么问题。

5个回答

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”更改为“false”非常重要 :) - Ja͢ck

5
正确的返回值在递归函数调用链中丢失。找到正确的值后,任何额外的搜索都将从那一点开始返回错误的值。
处理这个问题有几种方法:
1. 取消搜索
当找到正确的值时,立即将其返回到递归堆栈的最上层,而不搜索当前数组或嵌套数组的任何其他部分。换句话说,取消搜索的其余部分。
@Barmer的答案是一个例子。他代码的关键部分是使用for循环而不是each方法来迭代数组,因为中断for循环要容易得多。
2. 将值存储在安全的地方
当找到正确的值时,将其存储在安全的地方,允许搜索的其余部分继续进行,并在初始函数调用完成后访问该值。最简单的方法是将正确的值存储在全局变量中,尽管这不是一个好的做法,因为它违反了函数的封装性。
@shyam的答案提供了一个更清晰的解决方案:将对全局变量的引用作为函数参数传递,当找到正确的值时设置参数,然后在初始函数调用完成后访问全局变量。
两种方法之间的选择
用通俗易懂的语言来说,函数的预期逻辑可以总结如下:当你找到你要找的东西时,停下来,立即让我知道它是什么。继续搜索的唯一原因是需要找到多个数据片段。我假设这里不是这种情况。
在这两种方法中,第二种方法是一个快速修复的解决方法,应该可以正常工作,但会进一步混淆任何试图理解函数预期逻辑的人。如果只寻找已经找到的单个数据片段,为什么搜索还在继续呢?
第一种方法是对函数进行重构,使其更符合预期的逻辑,从而使函数更易于理解。当它找到所需内容时,函数停止搜索。

谢谢Matt!!你能否再详细解释一下选项1? - Aneesh
@Aneesh:我更新了答案,更详细地解释了这两种方法,以及哪一种可能更好。 - Matt Coughlin

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);
    }

被接受的答案确实为您提供了部分答案,但没有解释原因。因此,本答案


那个forEach的东西把我的一天都搞糟了。谢谢! - Deepak

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)

谢谢Shyam!但是在这种情况下,函数也会一直递归到结束。对吗? - Aneesh
1
是的。如果你想立即停止,你必须使用循环而不是使用forEach。 - shyam

0

这里有一个使用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}
<script src="https://bundle.run/object-scan@13.7.1"></script>

声明:本人是object-scan的作者。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接