繁体   English   中英

使用键值获取对 JSON/JS 对象内任意(深)嵌套节点的引用

[英]Get reference to arbitrarily (deep) nested node inside JSON/JS object using value of key

我整夜都在关注 SO,有很多类似的问题,但目前没有一个能直接解决我的问题。 所以请看下面。

我有一个形式的对象:

let data = [{
   "id": 777,
   "name": "Level 1_section_1",
   "children": [{
       "id": 778,
       "name": "Level 2a",
       "children": [

       ]
     },
     {
       "id": 783,
       "name": "Level 2b",
       "children": [

       ]
     }
   ]
 },
 {
   "id": 786,
   "name": "Level 1_section_2",
   "children": [{
     "id": 781,
     "name": "Level 2c",
     "children": [

     ]
   }]
 }
]

基本上,children 包含相同结构节点的数组。

如果我希望获得对包含id:783的节点的引用,我会直观地使用递归,但我不知道如何确保它递归地覆盖整个树,直到它找到并返回我想要的确切节点,以便我可以将更多子节点附加到找到的节点。

诚然,尽管我有 CS 背景,但我对递归的了解相当生疏。

这是我在我的 jsfiddle 中尝试过的: https ://jsfiddle.net/hanktrizz/surmf7dq/4/

请注意, data树可以任意深(尽管我不希望它超过 8 或 9 个深度级别)但只是想我会指出它。

这是一种可能性,在递归函数中使用for循环:

 let data=[{id:777,name:"Level 1_section_1",children:[{id:778,name:"Level 2a",children:[]},{id:783,name:"Level 2b",children:[]}]},{id:786,name:"Level 1_section_2",children:[{id:781,name:"Level 2c",children:[]}]}]; const findNode = (arr, idToFind) => { for (const item of arr) { if (item.id === idToFind) { return item; } const possibleResult = findNode(item.children, idToFind); if (possibleResult) { return possibleResult; } } }; console.log(findNode(data, 778));

这是一个更高阶的findNode ,它不仅限于按id搜索。 相反,它接受用户定义的 lambda 来使用任何条件搜索节点 -

findNode (n => n.id === 778, data)
// { id: 778, name: "Level 2a" }

findNode (n => n.name === "Level 2c", data)
// { id: 781, name: "Level 2c" }

findNode (n => n.id === 999, data)
// undefined

在您自己的浏览器中验证以下结果 -

 const data = [{id:777,name:"Level 1_section_1",children:[{id:778,name:"Level 2a",children:[]},{id:783,name:"Level 2b",children:[]}]},{id:786,name:"Level 1_section_2",children:[{id:781,name:"Level 2c",children:[]}]}]; const None = Symbol () // findNode : (node -> boolean, node array) -> node? const findNode = (f, [ node = None, ...nodes ]) => node === None ? undefined : find1 (f, node) || findNode (f, nodes) // find1 : (node -> boolean, node) -> node? const find1 = (f, node = {}) => f (node) === true ? node : findNode (f, node.children) console.log (findNode (n => n.id === 778, data)) // { id: 778, name: "Level 2a" } console.log (findNode (n => n.name === "Level 2c", data)) // { id: 781, name: "Level 2c" } console.log (findNode (n => n.id === 999, data)) // undefined

上面,析构赋值允许一个优雅的表达,但也会创建不必要的中间值。 以下修订是一项重大改进 -

// findNode : (node -> boolean, node array, int) -> node?
const findNode = (f, nodes = [], i = 0) =>
  i >= nodes.length
    ? undefined
    : find1 (f, nodes[i]) || findNode (f, nodes, i + 1)

// find1 : (node -> boolean, node) -> node?
const find1 = (f, node = {}) =>
  f (node) === true
    ? node
    : findNode (f, node.children)

两个版本都提供短路评估,并在找到第一个结果后立即停止迭代

为了好玩,这里有一个尝试返回所有实例。

 var data=[{id:777,name:"Level 1_section_1",children:[{id:778,name:"Level 2a",children:[]},{id:786,name:"Level 2b",children:[]}]},{id:786,name:"Level 1_section_2",children:[{id:781,name:"Level 2c",children:[]}]}] var f = (o, pred, acc=[]) => pred(o) ? [o] : Object.values(o).reduce((a, b) => b && typeof b == 'object' ? a.concat(f(b, pred, acc)) : a, acc) console.log(JSON.stringify(f(data, o => o.id == 781))) console.log(JSON.stringify(f(data, o => o.id == 786)))

这是使用对象扫描的迭代解决方案

主要优点是您可以访问filterFn其他数据,并且可以轻松地进行进一步处理。 显然,引入依赖是一种权衡

 // const objectScan = require('object-scan'); const myData = [{ id: 777, name: 'Level 1_section_1', children: [{ id: 778, name: 'Level 2a', children: [] }, { id: 783, name: 'Level 2b', children: [] }] }, { id: 786, name: 'Level 1_section_2', children: [{ id: 781, name: 'Level 2c', children: [] }] }]; const treeSearch = (data, id) => objectScan(['**(^children$)'], { useArraySelector: false, abort: true, rtn: 'value', filterFn: ({ value }) => value.id === id })(data); console.log(treeSearch(myData, 778)); // => { id: 778, name: 'Level 2a', children: [] }
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="https://bundle.run/object-scan@13.7.1"></script>

免责声明:我是对象扫描的作者

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM