递归 JavaScript 函数正在丢失返回值
Posted
技术标签:
【中文标题】递归 JavaScript 函数正在丢失返回值【英文标题】:Recursive JavaScript function is losing the return value 【发布时间】: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")
我不知道出了什么问题。
【问题讨论】:
【参考方案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
<script src="https://bundle.run/object-scan@13.7.1"></script>
免责声明:我是object-scan的作者
【讨论】:
以上是关于递归 JavaScript 函数正在丢失返回值的主要内容,如果未能解决你的问题,请参考以下文章