用 reduce 将平铺数组转为树形结构的核心是:先构建以 id 为键的节点映射表并初始化 children 数组,再将每个节点挂载到对应父节点的 children 中,最后筛选出 parentId 为空值(null/undefined/''/0)的根节点返回。

用 reduce 把平铺数组转成树形结构,核心是:按父子关系逐个归并节点,把子节点挂到对应父节点的 children 数组里,并最终找出根节点(即 parentId 不存在或为 null/undefined 的项)。
明确数据结构和关键字段
假设平铺数据长这样:
[{ id: 1, name: 'A', parentId: null },
{ id: 2, name: 'B', parentId: 1 },
{ id: 3, name: 'C', parentId: 1 },
{ id: 4, name: 'D', parentId: 2 }]
你需要确认三个字段:id(唯一标识)、parentId(指向父级 id)、children(用于存放子节点,需初始化为空数组)。如果字段名不同(比如叫 pid 或 parent_id),记得在代码里对应替换。
用 reduce 构建映射表 + 挂载子节点
不直接拼树,而是先用 reduce 建一个以 id 为键的对象映射(类似哈希表),同时给每个节点初始化 children: []。再遍历一次,把当前节点推入其父节点的 children 中。
立即学习“Java免费学习笔记(深入)”;
- 第一步:生成带
children的节点映射 - 第二步:对每个节点,若存在
parentId,就把它加进对应父节点的children数组 - 第三步:筛选出所有
parentId为空值(null、undefined、''或0等需按实际业务判断)的节点,作为根节点返回
写出可运行的 reduce 版本
下面是简洁可靠的实现(支持 parentId 为 null / undefined / 0 的常见情况,可根据需要调整空值判断):
function listToTree(list, idKey = 'id', pidKey = 'parentId', childrenKey = 'children') {
const map = {};
const roots = [];
list.reduce((acc, node) => {
const id = node[idKey];
const pid = node[pidKey];
// 初始化当前节点,确保有 children 数组
acc[id] = { ...node, [childrenKey]: acc[id]?.[childrenKey] || [] };
// 如果有父节点,挂载到父节点的 children 下
if (pid != null && acc[pid]) {
if (!acc[pid][childrenKey]) acc[pid][childrenKey] = [];
acc[pid][childrenKey].push(acc[id]);
} else {
// 否则视为根节点
roots.push(acc[id]);
}
return acc;
}, map);
return roots;
}
调用示例:listToTree(data) 就能得到树形数组。注意:该方法不修改原数组,返回的是新结构。
注意事项和常见坑
数据顺序不影响结果(reduce 会统一处理所有节点),但要确保 id 唯一且 parentId 指向的节点确实存在(否则子节点会丢失)。如果存在“父节点在子节点后面出现”的情况,上面写法仍能正确挂载——因为用了对象映射缓存所有节点,后续遇到父节点时,子节点早已在映射中,挂载逻辑依然成立。
如果后端返回的 parentId 是字符串类型(如 "1"),而 id 是数字,注意用严格相等(===)可能匹配失败,建议统一转成字符串比较,或提前转换数据类型。


















