将扁平数据结构转Tree

本文探讨了两种将扁平数据结构转换为树形结构的方法:递归和使用Map。在小数据量时两者性能相近,但随着数据量增加,递归方式的效率下降。示例代码分别展示了两种实现,并通过性能测试对比了它们在大数据量下的表现。对于大规模数据操作,使用Map可能更为高效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在项目中遇到一个问题

将后台返回的扁平数据结构,转成树

数据内容如下

 转换成大概像下面这个形式

    treeData: [
      {
        "id": 1,
        "name": "部门1",
        "pid": 0,
        "children": [
          {
            "id": 2,
            "name": "部门2",
            "pid": 1,
            "children": []
          },
          {
            "id": 3,
            "name": "部门3",
            "pid": 1,
            "children": [
              {
                "id": 4,
                "name": "部门4",
                "pid": 3,
                "children": []
              },
            ]
          }
        ]
      }
    ]

递归

首先想到的是递归

在上一篇已经研究过js的三种循环性能,所以这次遍历采用了foreach实现

  <script>
    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 },
    ]
    function transTree (list, rootValue) { // list: 整个数组, rootValue本次要查找的目标id -> 此函数为了找到rootValue目标id的下属们
      const treeData = [] // 装下属对象的
      list.forEach(item => {
        if (item.pid === rootValue) { // 当前对象pid符合, 继续递归调用查找它的下属
          const children = transTree(list, item.id) // 返回item对象下属数组
          if (children.length) {
            item.children = children // 为item添加children属性保存下属数组
          }
          treeData.push(item) // 把当前对象保存到数组里, 继续遍历
        }
      })
      return treeData // 遍历结束, rootValue的id对应下属们收集成功, 返回给上一次递归调用children, 加到父级对象的children属性下
    }
    let data = transTree(arr, 0)
    console.log(data);
  </script>

 Map

还有一个想法是通过Map,先把数据转成Map去存储,之后遍历的同时借助对象的引用,直接从Map找对应的数据做存储

    function transTree2 (items) {
      const result = [];   // 存放结果
      const Map = {};  
      items.forEach(item => {
        const id = item.id;
        const pid = item.pid;
        //若没有该部门,创建
        if (!Map[id]) {
          Map[id] = {
            children: [],
          }
        }
        //将该部门放入Map中
        Map[id] = {
          ...item,
          children: Map[id]['children']
        }

        const treeItem = Map[id];

        if (pid === 0) { //如果是一级部门直接插入结果数组中
          result.push(treeItem);
        } else {
          if (!Map[pid]) { //若没有一级部门,创建
            Map[pid] = {
              children: [],
            }
          }
          Map[pid].children.push(treeItem) //通过Map将该部门放入对应父部门中
        }
      })
      return result;
    }
    data = transTree2(arr)
    console.log(data);

 对比两种方式性能

mock模拟数据

const data = Mock.mock({
  'list|9999': [
    {
      name: '@cname',
      address: '@city(true)',
      'id|+1': 1,
      'pid|': function () {
        if (this.id % 4 === 0) {
          return this.id + 1
        }
        if (this.id % 2 === 0) {
          return this.id == 2 ? 0 : 2
        }
        else return this.id == 3 ? 0 : 3
      }
    }
  ]
})
const arr = data.list
console.log(arr)
function transTree (list, rootValue) { 
...
}
console.time('1')
let res = transTree(arr, 0)
console.timeEnd('1')
console.log(res)

function transTree2 (items) {
...
}
console.time('2')
res = transTree2(arr)
console.timeEnd('2')
console.log(res)

 

 

 

 

数据量小的时候差别不大,但是数据量变大采用递归方式实现会越来越慢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值