算法 一维数组转化为多维数组(已知id、父id和目录级别)
开发时可能会遇到这样的问题:
前端需要渲染多层级的目录列表,但是后端返回的却是一个扁平化的数组
返回数据如以下情况(已删除敏感数据):
LEVEL
:目录级别NODE_ID
:当前节点的标识P_NODE_ID
:父节点的标识
const list = [
{
LEVEL: 1,
NODE_ID: 294,
P_NODE_ID: 254,
},
{
LEVEL: 1,
NODE_ID: 293,
P_NODE_ID: 254,
},
{
LEVEL: 2,
NODE_ID: 299,
P_NODE_ID: 294,
},
{
LEVEL: 3,
NODE_ID: 389,
P_NODE_ID: 299,
},
{
LEVEL: 2,
NODE_ID: 354,
P_NODE_ID: 293,
},
{
LEVEL: 3,
NODE_ID: 342,
P_NODE_ID: 354,
},
];
如果后端无法修改或者打死不改,那么只能由前端进行处理
解决思路与方法
看到这种问题,脑中的第一个想法就是暴力破解:
但是每次查找父节点,我们一般会调用find
方法,但是find
方法在查找的过程中会遍历整个数组直到找到符合条件的元素,数据过大,那么总体消耗的时间就会过长
上面的方法耗时长是因为每一个元素都需要找到自己的父节点,然后添加到父节点,一个父节点下可能有多个子节点,那么我们可以将上面的步骤进行反转:
- 将同父节点的元素添加到数组中
- 查找关系,将元素一并加入
同父节点的元素有一个相同的点——P_NODE_ID
(父节点标识相同),那么我们可以使用Map
或者对象进行存储
const map = new Map();
let res = []; // 存放最终结果
list.forEach((val) => {
let pid = val.P_NODE_ID; // 获取到父节点id
if (map.has(pid)) { // map中存在pid,表示该元素存在同父节的元素
map.set(pid, map.get(pid).push(val));
} else if (val.LEVEL === 1) {
// 第一层级不需要记录
res.push(val);
}else { // map中不存在pid,则需要进行初始化
map.set(pid, [val]);
}
});
存储完,接下来我们需要查找关系:
// 根据id和map进行查找
function findChildren(val, map) {
const children = map.get(val.NODE_CODE) || [];
children.map((val) => findChildren(val, map)); // 递归查找
val.children = children;
return val;
}
res = res.map((val) => {
return findChildren(val, map);
});
完整代码
function fn(list) {
const map = new Map();
let res = [];
// 记录
list.forEach((val) => {
let pid = val.P_NODE_CODE;
if (map.has(pid)) {
map.set(pid, map.get(pid).push(val));
} else if (val.LEVEL === 1) {
// 第一层级不需要记录
res.push(val);
} else {
map.set(pid, [val]);
}
});
// 根据id和map进行查找
function findChildren(val, map) {
const children = map.get(val.NODE_CODE) || [];
children.map((val) => findChildren(val, map));
val.children = children;
return val;
}
return res.map((val) => {
return findChildren(val, map);
});
}