用 reduce 将一维数组转为树形结构的核心是:先用哈希表缓存所有节点并初始化 children,再遍历中按 parentId 建立父子引用,最后筛选 parentId 为空的节点作为根。

用 reduce 把一维数组转成树形结构,核心是按父子关系(如 id 和 parentId)逐个归位节点,并建立引用关系。关键在于:先挂载所有节点到一个哈希表中,再用 reduce 一次性把子节点推入父节点的 children 数组,最后筛选出顶级节点(即 parentId 为空或为指定根值的项)。
明确数据结构和层级依据
假设原始数组每个对象有 id、parentId、name 等字段,其中 parentId 指向其父节点的 id;顶级节点的 parentId 通常为 null、undefined 或特定值(如 0 或 "root")。需提前确认这个“根标识”,它决定谁不被挂到任何父节点下。
用 reduce 构建映射 + 归集子节点
分两步在一次 reduce 中完成:
- 初始化一个空对象 map 存所有节点(以 id 为 key),并确保每个节点都有 children: []
- 遍历每一项时,先存入 map;再根据 parentId 找到父节点,把当前节点 push 进父节点的 children
- 即使父节点还没出现在数组前面,也要先初始化它(避免后续找不到而报错)
- 顶级节点不会被任何项作为子节点挂载,所以它们的
children保持初始空数组或按需填充 -
reduce的返回值就是最终的map,之后只需提取所有顶级节点即可
提取根节点并返回树形结构
遍历 map 的所有值,筛选出 parentId 符合根条件的节点(例如 item.parentId == null),这些就是树的顶层。注意:返回的是数组(可能多棵根),若业务要求单棵树且只允许一个根,可取第一个或加逻辑校验。
⚠️ 注意:不要在 reduce 内部直接修改原数组或依赖遍历顺序来构造嵌套——而是靠引用关系(对象地址)让子节点真实嵌入父节点的 children 数组中。
完整可运行示例
输入:
const list = [
{ id: 1, parentId: null, name: "首页" },
{ id: 2, parentId: 1, name: "产品" },
{ id: 3, parentId: 1, name: "关于我们" },
{ id: 4, parentId: 2, name: "Web端" },
{ id: 5, parentId: 2, name: "App端" }
];处理:
const tree = list.reduce((map, item) => {
const { id, parentId } = item;
// 确保当前节点存在且 children 初始化
if (!map[id]) map[id] = { ...item, children: [] };
// 确保父节点存在(即使还没出现过)
if (parentId != null && !map[parentId]) {
map[parentId] = { id: parentId, children: [] };
}
// 如果有父节点,把当前节点加入其 children
if (parentId != null && map[parentId]) {
map[parentId].children.push(map[id]);
}
return map;
}, {});
<p>const rootNodes = Object.values(tree).filter(node => node.parentId == null);
结果 rootNodes 即为树形 JSON 数组,结构清晰、引用正确、无重复拷贝。


















