数组转树的递归和非递归方法

96 阅读4分钟

1.数组转树用来干吗的

const data = [
    { 'id': '29', 'pid': '', 'name': '总裁办' },
    { 'id': '2c', 'pid': '', 'name': '财务部' },
    { 'id': '2d', 'pid': '2c', 'name': '财务核算部' },
    { 'id': '2f', 'pid': '2c', 'name': '薪资管理部' },
    { 'id': 'd2', 'pid': '', 'name': '技术部' },
    { 'id': 'd3', 'pid': 'd2', 'name': 'Java研发部' }
]

我们有时候需要将上面这部分无明显层级关系的数据转化成下面这部分有层级关系的数据 这个时候就需要用到将数组转化成树形结构的方法 也会更方便使用element-ui中的el-tree结构

const data = [
    { 'id': '29', 'pid': '', 'name': '总裁办', 'children': [] },
    {
      'id': '2c', 'pid': '', 'name': '财务部', 'children':
        [
          { 'id': '2d', 'pid': '2c', 'name': '财务核算部' },
          { 'id': '2f', 'pid': '2c', 'name': '薪资管理部' },
        ]
    },
    {
      'id': 'd2', 'pid': '', 'name': '技术部', 'children':
        [
          { 'id': 'd3', 'pid': 'd2', 'name': 'Java研发部' }
        ]
    },
]

2.数组转树递归方法

2.1核心思路

1.以上方数据为例 我们需要先找到pid为空的几个对象 并先将它们存入事先定义好的需要返回的树数组中 这样就先找到了几个顶层数据对象
2.接着我们需要遍历这个存储顶层数据对象的树数组 给每个顶层数据对象的children属性添加子节点数组(通过递归传入data和被遍历的每个顶层数据对象的id) 这样就可以给每个顶层数据对象的children子数组添加进pid与它们id相等的对象 也就是找到了下属对象

2.2完整代码

const data = [
      { 'id': '29', 'pid': '', 'name': '总裁办' },
      { 'id': '2c', 'pid': '', 'name': '财务部' },
      { 'id': '2d', 'pid': '2c', 'name': '财务核算部' },
      { 'id': '2f', 'pid': '2c', 'name': '薪资管理部' },
      { 'id': 'd2', 'pid': '', 'name': '技术部' },
      { 'id': 'd3', 'pid': 'd2', 'name': 'Java研发部' }
    ]
// 封装数组转树函数
    function arrayToTree(arr, pid) {
      // 筛选出pid为空的对象作为根数组
      const tree = arr.filter(item => item.pid === pid)
      // 遍历tree添加子节点
      tree.forEach(item => {
        // 通过递归将pid与顶层对象的id相同的对象添加进当前顶层对象的children子节点
        item.children = arrayToTree(arr, item.id)
      })
      return tree
    }
    // 调用函数传入需要转树的数组和初次为空的pid(为了先找到顶层数据对象)
    console.log(arrayToTree(data, ''))

2.3运行结果

1.png

3.数组转树非递归方法

3.1核心思路

1.定义转完后的树tree为空数组 定义idMap存放每个对象的id及其对应的对象(键值对)

2.遍历需要转的原始数组data 给每个对象添加children属性 并在idMap在中存入每个对象的id及其对应的对象(键值对)

3.再次遍历data 定义parent来存放idMap中属性名item.pid对应的对象

4.如果parent存在就说明当前数据不是顶层的数据 需要添加进对应id的children子节点数组

5.如果parent不存在就说明是顶层数据 就直接添加进tree中

3.2完整代码

const data = [
      { 'id': '29', 'pid': '', 'name': '总裁办' },
      { 'id': '2c', 'pid': '', 'name': '财务部' },
      { 'id': '2d', 'pid': '2c', 'name': '财务核算部' },
      { 'id': '2f', 'pid': '2c', 'name': '薪资管理部' },
      { 'id': 'd2', 'pid': '', 'name': '技术部' },
      { 'id': 'd3', 'pid': 'd2', 'name': 'Java研发部' }
    ]

    // 封装数组转树函数
    function arrayToTree(arr) {
      const tree = [] // 转完后的树
      const idMap = {} // 存放每个对象的id及对应的对象(键值对)
      // 遍历
      arr.forEach(item => {
        item.children = [] // 添加子节点数组
        idMap[item.id] = item // 添加id及对应对象
      })
      // 遍历添加进每个子节点数组
      arr.forEach(item => {
        // 存放每个pid对应的对象方便接下来的判断
        const parent = idMap[item.pid]
        // 判断
        if (parent) { // 如果parent存在就说明当前数据不是顶层的数据 需要添加进对应id的children子节点数组
          parent.children.push(item)
        } else { // 如果parent不存在就说明是顶层数据 就直接添加进tree中
          tree.push(item)
        }
      })
      return tree
    }

    console.log(arrayToTree(data))

非递归方法总结: arr循环中的item地址相同 所以parent.children.push(item)会影响tree中的每个顶层对象(简单来说 parent.children.push(item)会push到tree中每个顶层对象中的children子节点 因为地址相同会影响)

在return tree之前打印一下arr和最后打印一下data就可以明显看到arr和data都受到了地址带来的影响 如下图第一张所示

3.3运行结果

2.png 1.png

希望对你有帮助