JavaScript中嵌套对象结构中的递归树搜索
Posted
技术标签:
【中文标题】JavaScript中嵌套对象结构中的递归树搜索【英文标题】:Recursive tree search in a nested object structure in JavaScript 【发布时间】:2019-02-03 14:11:03 【问题描述】:我试图弄清楚如何递归地在这个 JSON 对象中搜索一个节点。我尝试了一些东西但无法得到它:
var tree =
"id": 1,
"label": "A",
"child": [
"id": 2,
"label": "B",
"child": [
"id": 5,
"label": "E",
"child": []
,
"id": 6,
"label": "F",
"child": []
,
"id": 7,
"label": "G",
"child": []
]
,
"id": 3,
"label": "C",
"child": []
,
"id": 4,
"label": "D",
"child": [
"id": 8,
"label": "H",
"child": []
,
"id": 9,
"label": "I",
"child": []
]
]
;
这是我的非工作解决方案,这可能是因为第一个节点只是一个值,而孩子们在数组中:
function scan(id, tree)
if(tree.id == id)
return tree.label;
if(tree.child == 0)
return
return scan(tree.child);
;
【问题讨论】:
【参考方案1】:您的代码只是缺少一个循环来检查child
数组中节点的每个子节点。此递归函数将返回节点的label
属性,如果树中不存在标签,则返回undefined
:
const search = (tree, target) =>
if (tree.id === target)
return tree.label;
for (const child of tree.child)
const found = search(child, target);
if (found)
return found;
;
const tree = "id":1,"label":"A","child":["id":2,"label":"B","child":["id":5,"label":"E","child":[],"id":6,"label":"F","child":[],"id":7,"label":"G","child":[]],"id":3,"label":"C","child":[],"id":4,"label":"D","child":["id":8,"label":"H","child":[],"id":9,"label":"I","child":[]]];
console.log(search(tree, 1));
console.log(search(tree, 6));
console.log(search(tree, 99));
您也可以使用不会导致堆栈溢出的显式堆栈进行迭代(但请注意,由于扩展语法,速记 stack.push(...curr.child);
可能会溢出某些 JS 引擎的参数大小,因此请使用显式循环或 concat
用于大型子数组):
const search = (tree, target) =>
for (const stack = [tree]; stack.length;)
const curr = stack.pop();
if (curr.id === target)
return curr.label;
stack.push(...curr.child);
;
const tree = "id":1,"label":"A","child":["id":2,"label":"B","child":["id":5,"label":"E","child":[],"id":6,"label":"F","child":[],"id":7,"label":"G","child":[]],"id":3,"label":"C","child":[],"id":4,"label":"D","child":["id":8,"label":"H","child":[],"id":9,"label":"I","child":[]]];
for (let i = 0; ++i < 12; console.log(search(tree, i)));
更通用的设计会返回节点本身,并让调用者访问.label
属性,如果他们愿意,或者以其他方式使用对象。
请注意,JSON 纯粹是一种用于序列化(字符串化、原始)数据的字符串格式。一旦您将 JSON 反序列化为 javascript 对象结构(如此处所示),它就不再是 JSON。
【讨论】:
【参考方案2】:scan
可以使用第三个参数递归编写,该参数模拟要扫描的节点队列
const scan = (id, tree = , queue = [ tree ]) =>
// if id matches node id, return node label
id === tree.id
? tree.label
// base case: queue is empty
// id was not found, return false
: queue.length === 0
? false
// inductive case: at least one node
// recur on next tree node, append node children to queue
: scan (id, queue[0], queue.slice(1).concat(queue[0].child))
因为 JavaScript 支持默认参数,所以 scan
的调用站点不会改变
console.log
( scan (1, tree) // "A"
, scan (3, tree) // "C"
, scan (9, tree) // "I"
, scan (99, tree) // false
)
在下面的浏览器中验证它是否有效
const scan = (id, tree = , queue = [ tree ]) =>
id === tree.id
? tree.label
: queue.length === 0
? false
: scan (id, queue[0], queue.slice(1).concat(queue[0].child))
const tree =
id: 1
, label: "A"
, child:
[ id: 2
, label: "B"
, child:
[ id: 5
, label: "E"
, child: []
, id: 6
, label: "F"
, child: []
, id: 7
, label: "G"
, child: []
]
, id: 3
, label: "C"
, child: []
, id: 4
, label: "D"
, child:
[ id: 8
, label: "H"
, child: []
, id: 9
, label: "I"
, child: []
]
]
console.log
( scan (1, tree) // "A"
, scan (3, tree) // "C"
, scan (9, tree) // "I"
, scan (99, tree) // false
)
相关recursive search using higher-order functions
【讨论】:
【参考方案3】:这是使用object-scan的解决方案
// const objectScan = require('object-scan');
const tree = "id":1,"label":"A","child":["id":2,"label":"B","child":["id":5,"label":"E","child":[],"id":6,"label":"F","child":[],"id":7,"label":"G","child":[]],"id":3,"label":"C","child":[],"id":4,"label":"D","child":["id":8,"label":"H","child":[],"id":9,"label":"I","child":[]]];
const search = (obj, id) => objectScan(['**.id'],
abort: true,
filterFn: ( value, parent, context ) =>
if (value === id)
context.push(parent.label);
return true;
return false;
)(obj, [])[0];
console.log(search(tree, 1));
// => A
console.log(search(tree, 6));
// => F
console.log(search(tree, 99));
// => undefined
.as-console-wrapper max-height: 100% !important; top: 0
<script src="https://bundle.run/object-scan@13.7.1"></script>
免责声明:我是object-scan的作者
【讨论】:
以上是关于JavaScript中嵌套对象结构中的递归树搜索的主要内容,如果未能解决你的问题,请参考以下文章