TypeScript 中的递归函数 - 父数组

Posted

技术标签:

【中文标题】TypeScript 中的递归函数 - 父数组【英文标题】:Recursive Function in TypeScript - Parents Array 【发布时间】:2022-01-22 11:38:17 【问题描述】:

我想创建一个递归函数来接收包含 id 和 parent_id 的对象列表。如果元素的父元素在列表中,我想将其删除并将其添加到父元素。

转换这个:


  "id": 180,
  "children": [],
  "parent_id": 195,
  "name": "Object 180"
,

  "id": 193,
  "children": [],
  "parent_id": 180,
  "name": "Object 193"
,

  "id": 194,
  "children": [],
  "parent_id": 180,
  "name": "Object 194"


  "id": 199,
  "children": [],
  "parent_id": 187,
  "name": "Object 199"


  "id": 304,
  "children": [],
  "parent_id": 193,
  "name": "Object 304"

到这里:


  "id": 180,
  "children": [
    
      "id": 193,
      "children": [
        
          "id": 304,
          "children": [],
          "parent_id": 193,
          "name": "Object 304"
         
      ],
      "parent_id": 180,
      "name": "Object 193"
    ,
    
      "id": 194,
      "children": [],
      "parent_id": 180,
      "name": "Object 194"
    
  ],
  "parent_id": 195,
  "name": "Object 180"
,

  "id": 199,
  "children": [],
  "parent_id": 187,
  "name": "Object 199"

有时parent_id为null,父母的等级没有限制。

【问题讨论】:

结果是一个数组? 【参考方案1】:

因为 basarat 的回答没有考虑嵌套超过一层的项目。

这是一个创建具有任意嵌套深度的输出的解决方案:

const listToTree = (input) => 
  const map = new Map(input.map((item) => [item.id, item]));
  
  const output = [];
  for (const item of input) 
    if (map.has(item.parent_id)) 
      map.get(item.parent_id).children.push(map.get(item.id));
     else 
      output.push(map.get(item.id));
    
  
  return output;
;

const input = [
  
    "id": 180,
    "value": 10,
    "children": [],
    "parent_id": 195,
    "name": "Object 180"
  ,
  
    "id": 193,
    "value": 10,
    "children": [],
    "parent_id": 180,
    "name": "Object 193"
  ,
  
    "id": 194,
    "value": 10,
    "children": [],
    "parent_id": 180,
    "name": "Object 194"
  ,
  
    "id": 199,
    "children": [],
    "parent_id": 187,
    "name": "Object 199"
  ,
  
    "id": 304,
    "value": 10,
    "children": [],
    "parent_id": 193,
    "name": "Object 304"
  ,
  
    "id": 305,
    "value": 10,
    "children": [],
    "parent_id": 194,
    "name": "Object 304"
  
];

const output = listToTree(input);

console.log(output);

编辑:聚合值

如果您想沿结果树的祖先链聚合值,我建议您之后在单独的函数中执行此操作。这将使您的代码更简洁、更易于测试且更具可读性。

实现取决于您的输入数组是否已排序(子项在父项之前)。如果要处理无序输入,则必须遍历每个祖先链。

function aggregateValue(branch) 
  const children = branch.children || [];
  return children.reduce((sum, child) => sum + aggregateValue(child), branch.value || 0);


function aggregateValueAlongBranches(tree) 
  return tree.map((branch) => 
    return 
      ...branch,
      aggregatedValue: aggregateValue(branch),
      children: aggregateValueAlongBranches(branch.children),
    ;
  );


const input = [
  
    "id": 180,
    "value": 10,
    "children": [
      
        "id": 193,
        "value": 10,
        "children": [
          
            "id": 304,
            "value": 10,
            "children": [],
            "parent_id": 193,
            "name": "Object 304"
          
        ],
        "parent_id": 180,
        "name": "Object 193"
      ,
      
        "id": 194,
        "value": 10,
        "children": [
          
            "id": 305,
            "value": 10,
            "children": [],
            "parent_id": 194,
            "name": "Object 304"
          
        ],
        "parent_id": 180,
        "name": "Object 194"
      
    ],
    "parent_id": 195,
    "name": "Object 180"
  ,
  
    "id": 199,
    "children": [],
    "parent_id": 187,
    "name": "Object 199"
  
];
const output = aggregateValueAlongBranches(input);

console.log(output);

【讨论】:

谢谢!我在想我们需要一个递归函数。我可以在其中使用一个函数来堆叠孩子的价值吗?例如:parent_value = child_1.value + child_2.value 您的意思是为每个包含完整路径的项目添加一个附加字段?喜欢$rootItem.id.$child1.id.$child2.id 吗?使用ancestors 对树建模有一个常见的模式,请查看this。将其保存在如下字段中可能更有用:ancestors: ['root_ancestor_id', 'first_ancestor_id', 'second_ancestor_id']。意思是,这个项目是 'second_ancestor' 的子级,它是 'first_ancestor' 的子级,后者是没有父级的 'root_ancestor' 的子级。 我的意思是,例如,家庭拥有并添加到父母的金额。例如:在列表中,祖父有 4 美元,父亲有 2 美元,儿子有 1 美元。树转换后,祖父有 7 美元,父亲有 3 美元,儿子有 1 美元。所有父母都有孩子的钱。 @WalberAmorim,最好在单独的函数中执行此操作。我添加了一个例子 感谢您的帮助!你救了我:D【参考方案2】:

您不需要递归函数。只需跟踪您已经看过的项目,如果其中存在父项,请添加到 parent.children 或添加新的根节点。

附上一个完整的解决方案示例。

完整代码

type Item = 
    id: number,
    children: Item[],
    parent_id: number,
    name: string,


const items: Item[] = [
    
        "id": 180,
        "children": [],
        "parent_id": 195,
        "name": "Object 180"
    ,
    
        "id": 193,
        "children": [],
        "parent_id": 180,
        "name": "Object 193"
    ,
    
        "id": 194,
        "children": [],
        "parent_id": 180,
        "name": "Object 194"
    ,
    
        "id": 199,
        "children": [],
        "parent_id": 187,
        "name": "Object 199"
    ,
    
        "id": 304,
        "children": [],
        "parent_id": 193,
        "name": "Object 304"
    
];


function nest(items:Item[]): Item[] 
  const output: Item[] = [];
  const idToItem = new Map<number,Item>();
  for (let item of items) 
      // Either add to parent. Or create a new root level node
      if (idToItem.has(item.parent_id)) 
          idToItem.get(item.parent_id)!.children.push(item);
       else 
          idToItem.set(item.id, item);
          output.push(item);
      
  
  return output;


console.log(nest(items));

【讨论】:

此解决方案忽略嵌套子级

以上是关于TypeScript 中的递归函数 - 父数组的主要内容,如果未能解决你的问题,请参考以下文章

php写函数 根据子类(id)递归查找顶级父类(id) 返回父类名字 (表结构:id name pid)

java中,当实例化子类时会递归调用父类中的构造方法。这个说法对么?为啥

递归PHP函数写入外部数组

从数据库结果生成多维数组的递归函数

SQL递归获取所有父节点的函数

TypeScript中的嵌套递归类型提示?