为什么要用数组转树
在我们渲染后台数据时并不是所有的数据都是平铺直叙的,最典型的就是在后台管理平台上,一定会有的员工和部门的上下级管理关系,这是时候我们就需要使用数组转树形结构
数组转树形结构的两种方法:
1. 递归 (性能不好但实现简单)
/**
* 递归
* @param { Array } list 数组
* @param { String } parentId 父级 id
* @param { Object } param2 可配置参数
*/
const generateTree = (
list,
parentId = 0,
{ idName = "id", parentIdName = "parentId", childName = "children" } = {}
) => {
if (!Array.isArray(list)) {
throw new Error("type only Array");
// new Error("type only Array");
return list;
}
return list.reduce((pre, cur) => {
// 找到parentId 的子节点之后,递归找子节点的下一级节点
if (cur[parentIdName] === parentId) {
const children = generateTree(list, cur[idName]);
if (children.length) {
cur[childName] = children;
}
return [...pre, cur];
}
return pre;
}, []);
};
2. 非递归方式(性能好)
应用了对象保存的是引用的特点,每次将当前节点的 id 作为 key,保存对应节点的引用信息,遍历数组时,每次更新 objMap 的 children 信息,这样 objMap中保留了所有节点极其子节点,最重要的是,我们只需要遍历一遍数组
/**
* 非递归 (映射 + 引用)
* 前提:每一项都有parentId,根元素
* @param { Array } list 数组
* @param { String } rootId 根元素Id
* @param { Object } param2 可配置参数
*/
const generateTree2 = (
list,
rootId = 0,
{ idName = "id", parentIdName = "parentId", childName = "childern" } = {}
) => {
if (!Array.isArray(list)) {
new Error("type only Array");
return list;
}
const objMap = {}; //暂存数组以 id 为 key的映射关系
const result = []; // 结果
for (const item of list) {
const id = item[idName];
const parentId = item[parentIdName];
// 该元素有可能已经放入map中,(找不到该项的parentId时 会先放入map
objMap[id] = !objMap[id] ? item : { ...item, ...objMap[id] };
const treeItem = objMap[id]; // 找到映射关系那一项(注意这里是引用)
if (parentId === rootId) {
// 已经到根元素则将映射结果放进结果集
result.push(treeItem);
} else {
// 若父元素不存在,初始化父元素
if (!objMap[parentId]) {
objMap[parentId] = [];
}
// 若无该根元素则放入map中
if (!objMap[parentId][childName]) {
objMap[parentId][childName] = [];
}
objMap[parentId][childName].push(treeItem);
}
}
return result;
};
const res = generateTree2(array);
console.log("res", res);