尝试使用递归从嵌套的 javascript 对象树中提取项目

Posted

技术标签:

【中文标题】尝试使用递归从嵌套的 javascript 对象树中提取项目【英文标题】:Trying to extract items from a nested javascript object tree using recursion 【发布时间】:2020-09-26 10:10:58 【问题描述】:

我正在尝试学习递归,我有一个代码尝试提取所有父名称,如果他们有孩子,则将它们推送到数组中。

我的问题是我可以打印出所有的父母,但似乎只能推出 5 个中的 2 个。

function printChildrenRecursive(t) 
  let hasChildren = []

  if (t.children.length === 0) 
    return
  

  t.children.forEach(child => 
    if (child.children.length > 0) 
      console.log(child.name)
      hasChildren.push(child.name)
    
    printChildrenRecursive(child)
  )

  return hasChildren


const tree = 
  name: 'John',
  children: [
      name: 'Jim',
      children: [
        name: 'Joe',
        children: [
          name: 'Lee',
          children: []
        ]
      ]
    ,
    
      name: 'Zoe',
      children: [
          name: 'Kyle',
          children: [
            name: 'Tony',
            children: []
          ]
        ,
        
          name: 'Sophia',
          children: [
            name: 'Thor',
            children: []
          ]
        
      ]
    
  ]


let res = printChildrenRecursive(tree)
console.log(res);

console.log 输出

Jim
Joe
Zoe
Kyle
Sophia

但是 hasChildren.push 只输出

(2) ["Jim", "Zoe"]

【问题讨论】:

Is printChildrenRecursive 是一个奇怪的名字,它应该返回一个父数组。 我知道,我只是在学习有关打印儿童的教程,但在尝试修改代码时忘记了它。 【参考方案1】:

有两个地方你没有处理好应该返回的数组:

    在基本情况下,不只是return,而是return []

    在递归的情况下,不要忽略递归调用返回的值,而是将其附加到当前数组中。

这里是更正:

function printChildrenRecursive(t) 
  let hasChildren = [];
  if (t.children.length === 0) 
    return []; // always return an array!
  
  t.children.forEach(child => 
    if (child.children.length > 0) 
      console.log(child.name);
      hasChildren.push(child.name);
    
    // add the returned array: 
    hasChildren.push(...printChildrenRecursive(child));
  )
  return hasChildren;


const tree = name: 'John',children: [name: 'Jim',children: [name: 'Joe',children: [name: 'Lee',children: []]],name: 'Zoe',children: [name: 'Kyle',children: [name: 'Tony',children: []],name: 'Sophia',children: [name: 'Thor',children: []]]];

let result = printChildrenRecursive(tree);
console.log(result);

替代解决方案

该算法不返回根节点,因为它专注于 children -- 这可能是有意的。

输出顺序也是广度优先和深度优先的混合。更常见的递归顺序是深度优先顺序——比如预排序。

为什么不使用生成器。

这是一个实现:

function *dfs(node) 
    // If you want to include leaves, remove the condition:
    if (node.children.length) yield node.name; 
    for (let child of node.children) yield *dfs(child);


const tree = name: 'John',children: [name: 'Jim',children: [name: 'Joe',children: [name: 'Lee',children: []]],name: 'Zoe',children: [name: 'Kyle',children: [name: 'Tony',children: []],name: 'Sophia',children: [name: 'Thor',children: []]]];

let result = Array.from(dfs(tree));
console.log(result);

【讨论】:

约翰呢?应该也加了吧? @Sergey,显然询问者的意图不是获得根。他们的函数名暗示了这一点。【参考方案2】:

Trincot 已经说明了您的实施有什么问题。

我想指出,这可以写得更简单。这是一个简单的递归函数来收集所有父节点的名称:

const parents = (name, children) => 
  children .length > 0
    ? [name, ... children .flatMap (parents)]
    : []


const tree = name: 'John', children: [name: 'Jim', children: [name: 'Joe', children: [name: 'Lee', children: []]], name: 'Zoe', children: [name: 'Kyle', children: [name: 'Tony', children: []], name: 'Sophia', children: [name: 'Thor', children: []]]]

console .log (parents (tree))

如果某些节点可能没有children 属性,那只是稍微复杂一点:

const parents = (name, children) => 
  (children || []) .length > 0
    ? [name, ... children .flatMap (parents)]
    : []

如果您的要求是打印它们,那么您可以调用它并打印结果。我建议您养成将 I/O 与逻辑核心分开的习惯。

【讨论】:

以上是关于尝试使用递归从嵌套的 javascript 对象树中提取项目的主要内容,如果未能解决你的问题,请参考以下文章

如何递归搜索对象树并使用 JavaScript/Prototype 1.7 基于键/值返回匹配的对象

从嵌套的 javascript 对象中删除属性的最佳方法是啥?

递归遍历二叉树Javascript的所有嵌套子节点

从对象数组制作树,保留其他键值对[重复]

在Javascript中将数组转换为嵌套的JSON?

从函数中退出以减少激活对象比递归或调用嵌套函数更好吗?