列表数据转树状

案例需求

const arr = [
    { id: 'a', pid: '', name: '总裁办' },
    { id: 'b', pid: '', name: '行政部' },
    { id: 'c', pid: '', name: '财务部' },
    { id: 'd', pid: 'c', name: '财务核算部' },
    { id: 'e', pid: 'c', name: '税务管理部' }
];

把上面的数据转成下面的结果

const arr = [
    { id: 'a', pid: '', name: '总裁办' },
    { id: 'b', pid: '', name: '行政部' },
    {
        id: 'c',
        pid: '',
        name: '财务部',
        children: [
            { id: 'd', pid: 'c', name: '财务核算部' },
            { id: 'e', pid: 'c', name: '税务管理部' }
        ],
    },
];

递归实现与分析

const fn = (list, rootId) => {
    const arr = [];
    list.forEach(item => {
        if (item.pid === rootId) {
            const children = fn(list, item.id);
            if (children.length) {
                item.children = children;
            }
            arr.push(item);
        }
    });
    return arr;
};

上面是代码实现,看上去挺简单,但对于递归接触少的同学理解起来还是有难度的,为了方便分析代码的执行过程,我把上面递归函数进行了打散,作用是一样的,如下:

const fn1 = (list, rootId) => {
    const arr = [];
    list.forEach(item => {
        if (item.pid === rootId) {
            const children = fn2(list, item.id);
            if (children.length) {
                item.children = children;
            }
            arr.push(item);
        }
    });
    return arr;
};

const fn2 = (list, rootId) => {
    const arr = [];
    list.forEach(item => {
        if (item.pid === rootId) {
            const children = fn3(list, item.id);
            if (children.length) {
                item.children = children;
            }
            arr.push(item);
        }
    });
    return arr;
};

const fn3 = (list, rootId) => {
    const arr = [];
    list.forEach(item => {
        if (item.pid === rootId) {
        	// 代码执行不会用到 fn4,所以没有定义 fn4 也不会出错
            const children = fn4(list, item.id);
            if (children.length) {
                item.children = children;
            }
            arr.push(item);
        }
    });
    return arr;
};

调用 fn1 第 1 次循环

1、第 1 次调用 fn1,传递完整数据 list,此时 rootId 为 “”

2、新建数组,循环 list,判断第 1 项的 pid 是否等于 “”,发现相等,进入 if

3、调用 fn2,传递完整数据 list,rootId 为当前项的 id,就是 ‘a’

4、新建数组,循环 list,依次判断每一项的 pid 是否等于 ‘a’

5、发现没有 pid 等于 ‘a’,所以不会进入 if,等循环结束,返回空数组给 fn2,然后赋值给 children

6、此时 fn1 函数调用时的第 1 次循环,把 item 装进 arr

调用 fn1 第 2 次循环

1、循环 list,判断第 2 项的 pid 是否等于 “”,发现相等,进入 if

2、调用 fn2,传递完整数据 list,rootId 为当前项的 id,就是 ‘b’

3、新建数组,循环 list,依次判断每一项的 pid 是否等于 ‘b’

4、发现没有 pid 等于 ‘b’,所以不会进入 if,等循环结束,返回空数组给 fn2,然后赋值给 children

5、此时 fn1 函数调用时的第 2 次循环,把 item 装进 arr

调用 fn1 第 3 次循环

1、循环 list,判断第 3 项的 pid 是否等于 “”,发现相等,进入 if

2、调用 fn2,传递完整数据 list,rootId 为当前项的 id,就是 ‘c’

3、新建数组,循环 list,依次判断每一项的 pid 是否等于 ‘c’

4、发现第 4 项的 pid 等于 ‘c’,进入 if(注意调用 fn2 的循环还没有走完!)

5、调用 fn3,传递完整数据 list,rootId 为当前项的 id,就是 ‘d’

6、新建数组,循环 list,依次判断每一项的 pid 是否等于 ‘d’

7、发现没有 pid 等于 ‘d’,所以不会进入 if,等循环结束,返回空数组给 fn3,然后赋值给 children

8、此时 fn2 函数调用时的第 4 次循环,把 item 装进 arr,注意这个 arr 是 fn2 函数中的 arr

9、继续,发现第 5 项的 pid 等于 ‘c’,进入 if

10、调用 fn3,传递完整数据 list,rootId 为当前项的 id,就是 ‘e’

11、新建数组,循环 list,依次判断每一项的 pid 是否等于 ‘e’

12、发现没有 pid 等于 ‘e’,所以不会进入 if,等循环结束,返回空数组给 fn3,然后赋值给 children

13、此时 fn2 函数调用时的第 5 次循环,把 item 装进 arr,注意这个 arr 是 fn2 函数中的 arr

14、fn2 执行完毕返回的 arr 中就包含了最后 2 项,返回给 fn2 调用处

15、…

调用 fn1 第 4 次循环

继续循环到第 4 项、第 5 项的 pid 都不等于 “”,最终 fn1 函数返回前面收集好的 arr

其他方法

这个包 array-to-tree 也可以尝试下,比较简单!复杂场景请用 performant-array-to-tree

const arrayToTree = require('array-to-tree');
arrayToTree(arr, { parentProperty: 'pid', customID: 'id' }); // result is ok
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

当铺鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值