【问题标题】:Recursive Function in TypeScript - Parents ArrayTypeScript 中的递归函数 - 父数组
【发布时间】: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,父母的等级没有限制。

【问题讨论】:

  • 结果是一个数组?

标签: javascript typescript recursion


【解决方案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));

【讨论】:

  • 此解决方案忽略嵌套子级
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-04
  • 2018-11-03
  • 1970-01-01
  • 1970-01-01
  • 2013-11-05
  • 2021-11-10
  • 1970-01-01
相关资源
最近更新 更多