Skip to content
On this page

数组生成树的方法封装,开箱即用

早上有童鞋求助,后端又不按套路出牌了,直接给了list过来让他自己生成下树结构。我反手就把自己项目里封装好的方法CV了过去,然后就没有然后了,全剧终。

整体思路

  • 假设数组中每个数据的结构都是
    js
    {
        id: 1,
        parentId : 2
    }
  • 首先,最容易想到的方法就是每次取出一个数据,遍历一次数组找到这个parentId所指向的对象,添加一个children属性把当前对象塞进去,然后循环下去。最后找到数组中的所有根节点输出就完事了,复杂度是O(n2),是不是像极了插入排序,如果数据量不大已经足够了。
  • 如果项目中需要处理的数据量比较大,就要考虑降低复杂度了,介绍一下比较通用的做法,用空间换时间

实现

  1. 我们设置一个map来存储所有节点的children数组
ts
// 临时存储子节点数组的字典
  const temp = new Map<string | number, any>()
  1. 创建一个数组来存储根节点,避免最后还需要多遍历一次list来找出根节点
ts
// 储存根节点
  const result = []
  1. 循环整个list,从当前的data中取出parentId。如果parentId不存在(这里0也认为不存在,数据库里面主键一般不会是0),就认为这个节点是根节点,推入results数组。如果parentId存在,则放进map。最后把map中的值关联到data的children属性上。
ts
for (const data of list) {
    const { id,parentId } = data
    // 确保temp中存在该节点的子节点数组
    if (!temp.has(id)) temp.set(id, [])

    if (parentId) {
      // 如果parentId存在,放进map中对应的位置
      if (temp.has(parentId)) {
        temp.get(parentId).push(data)
      } else {
        temp.set(parentId, [data])
      }
    } else {
      // 如果parentId为undefined,null,0,"" 则认为是根节点,记录结果
      result.push(data)
    }

    // 绑定children
    data.children = temp.get(id)
  }
  1. 考虑项目中可能属性不是id parentId children这几个属性,做成配置项输入。
  2. 完工,整个数组只遍历了一次,复杂度在O(n)。

完整代码

ts
/**
 * List构建树方法
 * @param list 需要构建成树的List
 * @param props id对象自身标识 parentId对象父节点标识 children最后输出子节点数据的属性名
 * @returns 生成树后的根节点数组
 */
export const _structTree = (
  list: any[],
  props?: { id?: string; parentId?: string; children?: string },
) => {
  // 默认值
  const attrs = Object.assign(
    {
      id: 'id',
      parentId: 'parentId',
      children: 'children',
    },
    props,
  )

  // 临时存储子节点数组的字典
  const temp = new Map<string | number, any>()
  // 储存根节点,减少复杂度
  const result = []

  for (const data of list) {
    const { [attrs.id]: id, [attrs.parentId]: parentId } = data
    // 确保temp中存在该节点的子节点数组
    if (!temp.has(id)) temp.set(id, [])

    if (parentId) {
      // 如果parentId存在,放进map中对应的位置
      if (temp.has(parentId)) {
        temp.get(parentId).push(data)
      } else {
        temp.set(parentId, [data])
      }
    } else {
      // 如果parentId为undefined,null,0,"" 则认为是根节点,记录结果
      result.push(data)
    }

    // 绑定children
    data[attrs.children] = temp.get(id)
  }

  return result
}

上次更新于: