【JavaScript】手拿把掐,一文真正学会“扁平数据结构转Tree”

前言

多的不说,少的不唠,“扁平数据结构转tree”非常重要。我整理了一下这个算法的实现思路和实现过程。

咱们循序渐进,先不考虑性能,只实现功能,然后再慢慢优化!

另外,本期博客参与了【新星计划】,还请大家三连支持一下🌟🌟🌟感谢感谢💓💓💓

要实现的效果:

原始数据:

let arr = [
    {id: 1, name: '部门1', pid: 0}, 
    {id: 2, name: '部门2', pid: 1}, 
    {id: 3, name: '部门3', pid: 1}, 
    {id: 4, name: '部门4', pid: 3}, 
    {id: 5, name: '部门5', pid: 4}, 
]

输出结果:

[
    {
        "id": 1,
        "name": "部门1",
        "pid": 0,
        "children": [
            {
                "id": 2,
                "name": "部门2",
                "pid": 1,
                "children": []
            },
            {
                "id": 3,
                "name": "部门3",
                "pid": 1,
                "children": [
                    // 结果 ,,,
                ]
            }
        ]
    }
]

嵌套遍历

思路:

1. 看下图,因为对象属于引用数据类型,引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。所以当你操作对象时,只能操作对象在栈内存中的引用地址。比如下图,当源对象发生改变,那么浅拷贝出来的数据也会随之改变;

 2. 因为每个对象只有唯一的一个pid,也就是唯一的一个父级元素,所以我们只要嵌套两层遍历这个数组,就能将数组里的每个元素都找到自己的父元素,并插入到其chirden属性中。然后,因为对象的引用特性,就实现了扁平数据转成tree的效果。

function queryChilrden(arr) {
    let result = []
    arr.forEach(s => {
       s.chilrden=[]
            arr.forEach(d=>{
                 if(s.id==d.pid){
                     s.chilrden.push(d)
                 }
            })
       })
    return arr.find(item => item.pid === 0)
}

递归实现

我想大部分人应该第一反应就是用递归实现了。

思路:

1. 首先我们从最外层开始遍历,第一级是pid等于0的元素;

2. 然后递归查找pid等于上一级id的元素

实现代码:

function foo(list) { 
    let result = [];                 // 结果集
    getChildren(list, result, 0);    // 开始递归查找子元素,初始传入pid为0
    return result;
}
 
/**
 * @param {*} data   源数组
 * @param {*} result 将结果添加到这个数组
 * @param {*} pid    父id
 */
function getChildren(data, result, pid) {
    for (let item of data) {                        // 遍历传入的数组
       if (item.pid === pid) {                      // 如果当前元素的pid等于传进来的父id,则
          // 将当前对象所有属性和新增属性chilrden合并成一个新对象
          let newItem = {...item, chilrden : []};   
          result.push(newItem);                     // 将新对象添加的传进来的数组中
          // 将新对象的chilrden作为结果数组传入,下一层的结果则会添加到这一层对象的chilrden中
          getChildren(data, newItem.chilrden, item.id);                                                
       }
    }
}

优点: 

实现简单,可读性好

缺点: 

如果数据量过大,性能会比较差

利用map结构实现(性能最佳)

思路:

1. 注意这里说的map不是指es6的map数据结构,当然了,你用其实也可以。主要是要把元素的每一项改造成 { id: {...原本的对象,chirden:[] }  }这种结构的。例如:

把 

var arr = [{ id: 1, name: '部门1', pid: 0  },{ id: 2, name: '部门2', pid: 1  }]

改造成

{

    1 : { id: 1, name: '部门1', pid: 0, chirden: []  },

    2 : { id: 2, name: '部门2', pid: 1, chirden: []  } 

}

2. 实现思路的话其实和第一种嵌套遍历差不多,也是利用了对象的引用。只不过我们利用这种结构的数据,可以减少一层遍历。

实现代码:

function mapFun(arr){
            let mapItem = {}
            let result = []
            arr.forEach(item=>{
                mapItem[item.id] ={...item,chilrden:[]}
            })
            arr.forEach(s=>{
                if(s.pid === 0 ){
                    result.push(mapItem[s.id])
                }else{
                    if(mapItem[s.pid]){
 //用s.pid为key去mapItem里找,如果找到了。就说明当前这一项在mapItem里有父亲,然后就用父亲将当前s push进去。
                        mapItem[s.pid].chilrden.push(mapItem[s.id])
                    }
                }
            })
            return result
        }

最后

我觉得"扁平数据结构转Tree"这个题还是挺老生常谈的,而且平常工作中也偶尔会用到。面试也经常考。所以大家还是要抽时间琢磨明白的。其实最主要的是不能死记,是要明白它的逻辑,然后以后遇到类似的问题,也能触类旁通,这样才算是真正提升了自己!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

前端阿彬

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

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

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

打赏作者

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

抵扣说明:

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

余额充值