嵌套递归对象循环

时间:2021-02-16 20:57:45

标签: javascript arrays loops object recursion

我有对象数组,它们也可以有自己的数组。我的主要目标是在整个树中找到一个具有给定 id 的对象,并通过显示它出现的对象名称的名称来获取该元素的 readmap。

例如我有这样的数据对象:

{
  id: '0',
  name: "Boys"
  children: [
   {
    name: "Soldiers",
    children: [
     {
       name: "Bravo"
       children: [
         {name: "Tom"},
         {name: "Andrew"}
       ]
     }
    ]
   },
   {
    name: "Runners",
    children: [
     {
       name: "Team B"
       children: [
         {name: "Mark"},
         {name: "David"}
       ]
     }
    ]
   }
  ]
}

我目前正在通过函数查找项目

 function findByName (name, array) {
    for (const node of array) {
      if (node.name === name) return node;
      if (node.children) {
        const child = findByName(name, node.children);
        if (child) return child;
      }
    }
  }

但为了实现我的目标,我还需要实现该价值的路线图。例如。

当我想找到 "Tom" 时。除了 findByName 的结果,我还想得到 {name: "Tom", road: ["Boys", "Soldiers", "Bravo"]

3 个答案:

答案 0 :(得分:1)

您需要传递另一个处理路径的属性。首先将路径定义为空数组。并且由于您只关心名称,因此每次找到具有子节点的节点时,您都​​可以将名称推入该数组中。

然后您只需将更新后的数组传递给您的递归函数。请参阅下面的工作示例:

(我更新了你的函数以返回一个包含结果和路径的对象)

function findByName(name, array, path = []) {
  for (const node of array) {
    if (node.name === name) return {result: node, path};
    if (node.children) {
      path.push(node.name) // We update the path with the current node name that has children
      const child = findByName(name, node.children, path );
      if (child) return { result: child, path};
    }
  }
}

演示: https://jsitor.com/VnktoLq49

答案 1 :(得分:1)

您可以在调用函数中为每个级别添加路径,而无需移交路径。

const
    findByName = (array, name) => {
        for (const node of array) {
            if (node.name === name) return { ...node, path: [] };
            if (node.children) {
                const child = findByName(node.children, name);
                if (child) return { ...child, path: [node.name, ...child.path] };
            }
        }
    },
    data = [{ id: '0', name: "Boys", children: [{ name: "Soldiers", children: [{ name: "Bravo", children: [{ name: "Tom" }, { name: "Andrew" }] }] }, { name: "Runners", children: [{ name: "Team B", children: [{ name: "Mark" }, { name: "David" }] }] }] }];

console.log(findByName(data, 'Tom'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 2 :(得分:1)

我喜欢此类或问题的生成器,因为它允许您select 一个、多个或所有结果。此外,生成器将控制权交给调用者,允许您在对结果满意时停止搜索。这可以通过一个函数来完成 -

function* select(a = [], query = Boolean, path = [])
{ for (const t of a)
  { if (query(t)) yield { ...t, path }
    yield *select(t.children, query, [...path, t.name])
  }
}

const data =
  [{ id: '0', name: "Boys", children: [{ name: "Soldiers", children: [{ name: "Bravo", children: [{ name: "Tom" }, { name: "Andrew" }] }] }, { name: "Runners", children: [{ name: "Team B", children: [{ name: "Mark" }, { name: "David" }] }] }] }]

// select "Tom" OR "Mark"
for (const r of select(data, v => v.name == 'Tom' || v.name == "Mark"))
  console.log("found:", r)
  

found: {
  "name": "Tom",
  "path": [
    "Boys",
    "Soldiers",
    "Bravo"
  ]
}
found: {
  "name": "Mark",
  "path": [
    "Boys",
    "Runners",
    "Team B"
  ]
}

如果您只想要第一个结果,我们可以使用returnbreak,搜索会立即停止,这可能会节省许多浪费的计算-

function first (it)
{ for (const x of it)
    return x              // <- return and stop searching
}

first(select(data, v => v.name == "Andrew"))
{
  "name": "Andrew",
  "path": [
    "Boys",
    "Soldiers",
    "Bravo"
  ]
}

如果您想要所有结果,我们可以使用Array.from。因为 select 是灵活的,它允许我们做各种有用的查询 -

Array.from(select(data, v => !v.children), r => r.name)
[
  "Tom",
  "Andrew",
  "Mark",
  "David"
]