平铺的数组转成树形结构 Array --> Tree

406 阅读3分钟

本文介绍把平铺的数组转成数形数组的两种方法,分别是非递归和递归方法,包含代码和具体的推演过程

我们这里的平铺数组有三个属性:id、pid、name;其中pid决定着这个对象的父级,id决定着这个对象的子级,口有阐述可能难以表达清楚,我们看一下下面的小例子:

 [
   {id:"01", pid:"",   name:"老王" }, // 没有pid代表没有父级,这个对象是顶级
   {id:"02", pid:"01", name:"小张" } 
 ]
 // 上面的结构说明: 老王是小张的上级

一、非递归方法:

优点是相对来说更好理解,缺点是会改变原数组

// 把平铺的数组 转成 树形结构
const function arrryToTree(arr) {
  // 1. 定义两个变量
  const treeList = [] // 树状数据数组
  const map = {} // 存储映射关系
 
  // 2. 建立一个映射关系,并给每个元素补充children属性,key是id值,value是对象本身
  // 映射关系: 目的是让我们能通过id快速找到对应的元素
  // 补充children:让后边的计算更方便
  arr.forEach(item => {
    if (!item.children) {
      item.children = []
    }
    map[item.id] = item
  })
 
  // 3. 对于每一个元素来说,先找它的上级
      // 如果能找到,说明它有上级,则要把它添加到上级的children中去
      // 如果找不到,说明它没有上级,直接添加到 treeList
  arr.forEach(item => {
    const parent = map[item.pid]
    
    if (parent) {
      parent.children.push(item) // 如果存在上级则表示item不是最顶层的数据
    } else {
      treeList.push(item) // 如果不存在上级 则是顶层数据,直接添加
    }
  })
  return treeList // 返回
}

上述代码的推衍过程

// 1. 原平铺数组对象
arr = [
        { 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研发部'}
      ]      
 
// 2. 将id作用key建立映射关系 并补充children属性
treeList: []
map: {
  '29': { id: '29', pid: '', name: '总裁办', children:[] },
  '2c': { id: '2c', pid: '', name: '财务部', children:[] }
  '2d': { id: '2d', pid: '2c', name: '财务核算部', children:[] },
  '2f': { id: '2f', pid: '2c', name: '薪资管理部', children:[] },
  'd2': { id: 'd2', pid: '',  name: '技术部', children:[] },
  'd3': { id: 'd3', pid: 'd2', name: 'Java研发部', children:[] }
}
 
// 3. 根据pid判断有无上级 生成树状数组
treeList: [
  { id: '29', pid: '', 'name': '总裁办' }
  { 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研发部'}]}
]

二、递归方法

优点是不hi改变原数组

function arrryToTree(arr,pid=''){
// 在list中根据pid来找元素
let treeList = []
// 过滤出来pid=''的就是顶层
treeList = arr.filter(item => item.pid === pid)
// 在通过循环加递归的方法将第二层和第三层放入到children中
treeList.forEach(item => {
   item.children = arrryToTree(arr,item.id)
})
    return treeList
}

上述代码的推衍过程

// 1. 原平铺数组对象
arr = [
        { 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研发部'}
      ]      
 
// 2. 根据pid过滤出顶层
treeList: [
    { id: '29', pid: '', name: '总裁办' },
    { id: '2c', pid: '', name: '财务部' },
    { id: 'd2', pid: '', name: '技术部'},
]
 
// 3. 通过循环,将pid===treeList中id的对象push到children中
treeList: [
  { id: '29', pid: '', 'name': '总裁办' }
  { 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研发部'}]}
]