JavaScript - 平行数据通过id-pid的形式 - 将其转换成树形结构

414 阅读4分钟

一 介绍

首先我们先看看一个正常的id-pid的数据结构:

     const arr = [
        { id: 12, pid: 1, name: "分公司1" },
        { id: 9999, pid: 0, name: "第二个总公司" },
        { id: 999901, pid: 9999, name: "第二个总公司的子公司" },
        { id: 1, pid: 0, name: "总公司" },
        { id: 121, pid: 12, name: "分公司1-1" },
        { id: 131, pid: 13, name: "分公司2-1" },
        { id: 133, pid: 13, name: "分公司2-3" },
        { id: 122, pid: 12, name: "分公司1-2" },
        { id: 13, pid: 1, name: "分公司2" },
        { id: 132, pid: 13, name: "分公司2-2" },
        { id: 123, pid: 12, name: "分公司1-3" },
        { id: 1231, pid: 123, name: "小分公司1122333" },
      ];

可以看到,我们要把他生成一个树形结构,而他是不止一个顶级节点的 (1 和 9999 ),也就是如下:

[
        {
          id: 1,
          pid: 0,
          name: "总公司",
          children: [
            {
              id: 12,
              pid: 1,
              name: "分公司1",
              children: [
                { id: 121, pid: 12, name: "分公司1-1" },
                { id: 122, pid: 12, name: "分公司1-2" },
                {
                  id: 123,
                  pid: 12,
                  name: "分公司1-3",
                  children: [{ id: 1231, pid: 123, name: "小分公司1122333" }],
                },
              ],
            },
            {
              id: 13,
              pid: 1,
              name: "分公司2",
              children: [
                { id: 131, pid: 13, name: "分公司2-1" },
                { id: 133, pid: 13, name: "分公司2-3" },
                { id: 132, pid: 13, name: "分公司2-2" },
              ],
            },
          ],
        },
        {
          id: 9999,
          pid: 0,
          name: "第二个总公司",
          children: [{ id: 999901, pid: 9999, name: "第二个总公司的子公司" }],
        },
      ];

二 第一步 ( 父id寻子pid )

我们应该声明一个新的空数组去存放我们的查找出来的数据以及声明另一个列表用来纳入已经是子节点的节点,并且2层遍历函数传进来的arr平行数据列表,同时每次声明一个children的数组列表,可以用来存放 当前的父id 的item下的children列表,在最后面去排除掉当前节点没有子组件的列表,代码如下:

  const list = [];
        for (let index = 0; index < arr.length; index++) {
          const item = arr[index];
          const children =  [];
          for (let index2 = 0; index2 < arr.length; index2++) {
            const item2 = arr[index2];
            if (item2.pid === item.id) {
              children.push(item2);
              hasId.push(item2.id);
            }
          }
          if (children?.length > 0) {
            list.push({ ...item, children });
          }
        }

这个时候,我们就会得到并且有子级节点的父级节点 ( 会排除掉那些没有子节点的节点 ),得出来的数据如下:

 [
        {
          id: 12,
          pid: 1,
          name: "分公司1",
          children: [
            { id: 121, pid: 12, name: "分公司1-1" },
            { id: 122, pid: 12, name: "分公司1-2" },
            { id: 123, pid: 12, name: "分公司1-3" },
          ],
        },
        {
          id: 9999,
          pid: 0,
          name: "第二个总公司",
          children: [{ id: 999901, pid: 9999, name: "第二个总公司的子公司" }],
        },
        {
          id: 1,
          pid: 0,
          name: "总公司",
          children: [
            { id: 12, pid: 1, name: "分公司1" },
            { id: 13, pid: 1, name: "分公司2" },
          ],
        },
        {
          id: 13,
          pid: 1,
          name: "分公司2",
          children: [
            { id: 131, pid: 13, name: "分公司2-1" },
            { id: 133, pid: 13, name: "分公司2-3" },
            { id: 132, pid: 13, name: "分公司2-2" },
          ],
        }
        ...... ( 这里省略掉了 )
      ]

三 第二步 (子pid寻父id)

现在我们来到了第二步,可以看到,我们已经把每个有children的节点都拿了出来,那我们就可以通过在去遍历这个列表,然后通过JS的特性,用pid找到id,在绑定来原有的节点上面;

     for (let index = 0; index < list.length; index++) {
          const item = list[index];
          for (let index2 = 0; index2 < list.length; index2++) {
            const item2 = list[index2];
            if (item.pid === item2.id) {
              let cIndex = item2.children.findIndex((v) => v.id === item.id);
              if (cIndex > -1) {
                item2.children[cIndex] = item;
              }
            }
          }
        }

四 第三步 过滤掉没有被存被子节点的节点 ( hasId:纳入子节点的节点列表 )

list.filter((v) => !hasId.includes(v.id));

五 收尾

这个时候其实我们就已经可以拿到最终的树形结构了,通过这几个步骤,我们就可以实现的是多个顶级节点的列表的转换,其实这里还有另外一个思路 就是把第二步的操作变成递归的形式去转换,但是同时也有一个缺点,就是只能兼容一个顶级节点 ( 或许是可以的,但是我目前还没有想到完美的解决方案 );

最终成型的代码如下:

   function toTreeFn(arr = [], hasId = []) {
        const list = [];
        for (let index = 0; index < arr.length; index++) {
          const item = arr[index];
          // const children = [];
          const children = [];
          for (let index2 = 0; index2 < arr.length; index2++) {
            const item2 = arr[index2];
            if (item2.pid === item.id) {
              children.push(item2);
              hasId.push(item2.id);
            }
          }
          if (children?.length > 0) {
            list.push({ ...item, children });
          }
        }
        // console.log(JSON.stringify(list));
        for (let index = 0; index < list.length; index++) {
          const item = list[index];
          for (let index2 = 0; index2 < list.length; index2++) {
            const item2 = list[index2];
            if (item.pid === item2.id) {
              let cIndex = item2.children.findIndex((v) => v.id === item.id);
              if (cIndex > -1) {
                item2.children[cIndex] = item;
              }
            }
          }
        }
        return list.filter((v) => !hasId.includes(v.id));
      }

最终转换成树形结构的效果:

[
        {
          id: 1,
          pid: 0,
          name: "总公司",
          children: [
            {
              id: 12,
              pid: 1,
              name: "分公司1",
              children: [
                { id: 121, pid: 12, name: "分公司1-1" },
                { id: 122, pid: 12, name: "分公司1-2" },
                {
                  id: 123,
                  pid: 12,
                  name: "分公司1-3",
                  children: [{ id: 1231, pid: 123, name: "小分公司1122333" }],
                },
              ],
            },
            {
              id: 13,
              pid: 1,
              name: "分公司2",
              children: [
                { id: 131, pid: 13, name: "分公司2-1" },
                { id: 133, pid: 13, name: "分公司2-3" },
                { id: 132, pid: 13, name: "分公司2-2" },
              ],
            },
          ],
        },
        {
          id: 9999,
          pid: 0,
          name: "第二个总公司",
          children: [{ id: 999901, pid: 9999, name: "第二个总公司的子公司" }],
        },
      ];

如果本文章对您的学习/工作有帮助,请点一个👍吧!!