数组生成树的方法封装,开箱即用
早上有童鞋求助,后端又不按套路出牌了,直接给了list过来让他自己生成下树结构。我反手就把自己项目里封装好的方法CV了过去,然后就没有然后了,全剧终。
整体思路
- 假设数组中每个数据的结构都是js
{ id: 1, parentId : 2 }
- 首先,最容易想到的方法就是每次取出一个数据,遍历一次数组找到这个parentId所指向的对象,添加一个children属性把当前对象塞进去,然后循环下去。最后找到数组中的所有根节点输出就完事了,复杂度是O(n2),是不是像极了插入排序,如果数据量不大已经足够了。
- 如果项目中需要处理的数据量比较大,就要考虑降低复杂度了,介绍一下比较通用的做法,用空间换时间
实现
- 我们设置一个map来存储所有节点的children数组
ts
// 临时存储子节点数组的字典
const temp = new Map<string | number, any>()
- 创建一个数组来存储根节点,避免最后还需要多遍历一次list来找出根节点
ts
// 储存根节点
const result = []
- 循环整个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)
}
- 考虑项目中可能属性不是id parentId children这几个属性,做成配置项输入。
- 完工,整个数组只遍历了一次,复杂度在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
}