使用对象构建树

假设我们有一个树数据结构
在这里插入图片描述

在应用程序中,以以下格式存储此信息是相当普遍的,尤其是在存在一对多父/子节点关系的情况下:

const data = [
  { id: 56, parentId: 62 },
  { id: 81, parentId: 80 },
  { id: 74, parentId: null },
  { id: 76, parentId: 80 },
  { id: 63, parentId: 62 },
  { id: 80, parentId: 86 },
  { id: 87, parentId: 86 },
  { id: 62, parentId: 74 },
  { id: 86, parentId: 74 },
];

那么,如何从这种对象数组格式转变为分层树格式?当您利用JavaScript对象引用时,这实际上变得相当容易。无需递归即可在O(n)时间内完成。

数组中的每个元素都是一个“节点”。一个节点可以是多个节点的“父级”,也可以是一个节点的“子级”。在上面的图片,node 86是node 80和node 87的“父”。node 86是的“孩子” node 74。我们树的顶部节点是“根”。

思考

要构建我们的树,我们将要:

  1. 遍历data数组
  2. 查找当前元素的父元素
  3. 在父元素的对象中,添加对子元素的引用
  4. 如果某个元素没有父元素,那么我们知道这将是树的“根”元素

但让我们开始创建元素ID到数组索引的映射。这将有助于我们在时机成熟时向元素的父级添加引用。

const idMapping = data.reduce((acc, el, i) => {
  acc[el.id] = i;
  return acc;
}, {});

id在data数组中的位置,那么我们要找到parentId对应的id在数组中的索引位置

id是56的在数组中的位置是0
parentId对应的id是62,在数组中的位置是7

该映射将如下所示。您很快就会知道为什么这样做有帮助。

{
  56: 0,
  62: 7,
  63: 4,
  74: 2,
  76: 3,
  80: 5,
  81: 1,
  86: 8,
  87: 6,
};

创建树

我们准备创建我们的树!让我们遍历对象,并为每个项目的父项分配引用。请注意我们在哪里使用我们idMapping来帮助我们找到父母。

let root;
data.forEach(el => {
  // 根节点
  if (el.parentId === null) {
    root = el;
    return;
  }
  // 父节点
  const parentEl = data[idMapping[el.parentId]];
  // 子节点
  parentEl.children = [...(parentEl.children || []), el];
});

得到结构

{
  id: 74,
  parentId: null,
  children: [
    {
      id: 62,
      parentId: 74,
      children: [{ id: 56, parentId: 62 }, { id: 63, parentId: 62 }],
    },
    {
      id: 86,
      parentId: 74,
      children: [
        {
          id: 80,
          parentId: 86,
          children: [{ id: 81, parentId: 80 }, { id: 76, parentId: 80 }],
        },
        { id: 87, parentId: 86 },
      ],
    },
  ],
};

为什么这样做

理解为什么这样做的最好方法是记住data数组的每个元素都是对内存中对象的引用el,我们forEach循环中的变量引用内存中的对象(data数组元素所引用的内存中的对应对象) ,并且parentEl还引用了内存中的对象(同样,也是data数组中被引用的对象之一)。

如果内存中的对象具有子级引用数组,则这些子级可以具有自己不断增长的子级引用数组。由于这些都是通过引用完成的,因此在修改其子项之一时,您无需告诉父母任何信息。

发布了254 篇原创文章 · 获赞 200 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/wu_xianqiang/article/details/103897915
今日推荐