TLDR; 如果我有一个包含对象的数组,而其中一些对象是包含更多对象的数组,我如何递归地遍历这些对象并返回其中一个嵌套值?在下面的代码中,using return仅解析第一个元素。如果我不使用return,那么该值会丢失,因为它在递归范围之一中被破坏了。
var currentNode = [{...}, {...}, { items: [{...}, {...}] }]
// If it's not found, looping through the children (there should be more wrapper nodes in the children)
for (let node of currentNode) {
if (node.uuid === uuidToFind) {
return node;
}
// THIS CAN'T LOOP
// BUT there's a lexical scoping problem if I don't use `return`
return searchTreeForNode(node, nodeUuidToFind);
}
}
详细摘要:
我目前正在处理当前以Vuex状态保存的树状结构。
在进入问题之前,我有两个问题:
另外,为了快速参考,我有一个JSFiddle demo here的副本。
使用以下尝试的解决方案,我在递归函数(特别是最后一个for of
循环)中无法进行词法作用域定义。
但是,让我从描述树开始。它有两种不同的节点。
看起来像这样的individual node
:
{
name: `string`
value: `string`
}
还有一个wrapper node
。 wrapper node
中的子级可以是individual node
或wrapper node
。
其结构如下:
{
type: `string`,
children: [
{},
{},
...
]
}
这些节点也可以嵌套无数次。
这是一个示例对象:
{
type: `level 1`,
children: [
{
type: `level 2`,
children: [
{
type: `level 3`,
children: []
},
{
name: `item 1`,
value: `value 1`
},
{
name: `item 2`,
value: `value 2`
},
...
]
},
{
name: `item 1`,
value: `value 1`
},
{
type: `level 2.1`,
children: [
{
name: `item 3`,
value: `value 3`
}
...
]
}
...
]
}
有了这棵树,我希望能够在树的任何位置添加individual nodes
和wrapper nodes
,但是这样做很麻烦。
我最初的尝试是遍历每个节点并为其分配一个UUID。计划是遍历树,当我找到匹配的UUID时,可以根据需要进行操作。
这是该尝试的代码:
// This starts with the top-level wrapper node
function searchTreeForNode(currentNode, nodeUuidToFind) {
if (currentNode.uuid === nodeUuidToFind) {
return currentNode;
}
// If it's a wrapper node, parse the children
if (currentNode.hasOwnProperty("type")) {
return searchTreeForNode(currentNode.children, nodeUuidToFind);
}
// If it's the contents of a wrapper node, see if that node lives in them
if (Array.isArray(currentNode)) {
let resolvedUuids = [];
for (let node of currentNode) {
resolvedUuids.push(node.uuid);
}
// If found, return the node
let uuidLocation = resolvedUuids.indexOf(nodeUuidToFind);
if (uuidLocation !== -1) {
return currentNode[uuidLocation];
}
// If it's not found, looping through the children (there should be more wrapper nodes in the children)
for (let node of currentNode) {
// THIS CAN'T LOOP
// BUT there's a lexical scoping problem if I don't use `return`
return searchTreeForNode(node, nodeUuidToFind);
}
}
}
是否可以使以上代码正常工作?具体来说是遍历包装节点的子节点吗?
如果没有,我现在的想法是要拥有三样东西,而不仅仅是让树处于状态。
nodeTree
-具有与原始数据相同形状的对象,但是每个节点只是一个UUID allRuleUuids
-字符串数组。每个字符串是节点的UUID nodeDataByUuid
-键入allRuleUuids
数组中的UUID的对象。每个对象将包含每个节点的数据。我需要支持的行为如下:
individual node
和wrapper node
。individual node
和wrapper node
(包括其所有子代)。在此先感谢您的帮助!
答案 0 :(得分:1)
您可以使用索引数组来表示节点的路径,例如:
[0, 1, 4]
好的,这是一个过于简化的实现。
function getNode(tree, path) {
let current = tree;
// find node
for (let i = 0; i < path.length; i++) {
current = current.children[path[i]];
}
return current;
}
function addNode(tree, path, node) {
const index = path.pop();
// ^ careful, this mutates the original array
// you can clone it if you wish
// or use string paths like `0/1/4`
// and use path.split('/') to get the indexes
const parent = getNode(tree, path);
parent.children[index] = node;
}
function deleteNode(tree, path) {
const index = path.pop();
const parent = getNode(tree, path);
delete parent.children[index];
}
// you can get nodes like this
console.log(getNode(tree, [1]));
// and add nodes like this
addNode(tree, [0, 3], { name: 'test', value: 'test' });
// you can get the root node like this
console.log(getNode(tree, []));
我没有处理给出错误路径的情况,即,假设路径[0, 1, 4]
中的所有索引都引用包装节点,但最后一个索引除外。但是你明白了。
答案 1 :(得分:1)
我更改了您的代码。这样就可以了
https://jsfiddle.net/v2rkj376/1/
function searchTreeForNode(currentNode, nodeUuidToFind) {
if (currentNode.uuid === nodeUuidToFind) {
return currentNode.name || 'untitled ('+currentNode.uuid+')';
}
// If it's a wrapper node, parse the children
if (currentNode.hasOwnProperty("type")) {
for(node of currentNode.children){
const foundNode = searchTreeForNode(node, nodeUuidToFind);
if(foundNode)
return foundNode;
}
}
}
更清洁的选择是将结构弄平,然后进行简单查找。
const flattenTree = node =>
node.children
? [node, ...node.children.flatMap(flattenTree)]
: [node];
然后发现变得简单:
flattenTree(sampleTree).find(node=>node.uuid === 'item5');
答案 2 :(得分:0)
我认为您需要这样的东西:
currentNode.forEach( singleNode => {
evalueateChildNodes(singleNode);
});
evaluateChildNodes(node) {
// You can check the id here and assign it to some variable if necessary
// Or if you found your specific node, you can push a new node to its children etc.
// Note that even though the function name is 'evaluateChildNodes',
// it will touch all root nodes due to the forEach() above.
if (node.children) {
evaluateChildNodes(node.children);
}
}
此代码将递归遍历整棵树并触摸每个节点。
或者,您可以将以上内容分配给变量,只要发现合适的内容就返回节点。
答案 3 :(得分:0)
我发现了我遇到的问题!我必须将值分配给一个临时变量,如果不是不确定的,则将其返回。
if (Array.isArray(tree)) {
for (let item of tree) {
let tmp = searchTreeForNode(item, nodeUuid);
if (tmp !== undefined) {
return tmp;
}
}
}