贝利信息

如何将三个扁平数组合并构建嵌套树形数据结构

日期:2026-01-21 00:00 / 作者:碧海醫心

本文介绍如何将通过多次 ajax 请求获取的三层扁平员工数据(根节点、一级子节点、二级子节点)按 `employeeid` 和父子关系动态组装为符合 treegrid 要求的嵌套树形结构,并提供可复用的递归挂载与扁平化工具函数。

在实际开发中(如使用 jqxTreeGrid、Ant Design Tree 或自定义树组件),常需将分层拉取的扁平数据构建成具有 children 属性的嵌套树结构。典型场景是:

关键挑战在于:如何根据业务逻辑(如 ReportsTo 字段或预定义层级映射)将子数组精准挂载到对应父节点的 children 属性中? 原始数据中并未显式包含 ReportsTo 字段,因此需依赖外部约定(例如:第二层数组中的所有项均属于第一层中 EmployeeID: 2 的子节点;第三层数组中的项均属于第二层中 EmployeeID: 5 的子节点)。以下提供两种主流实现方式:

✅ 方案一:基于层级顺序 + 显式挂载(推荐用于可控数据源)

假设你已知层级归属关系(如后端返回时附带 parentId 字段),最健壮的方式是统一建模后递归挂载:

// 模拟三组异步获取的数据
const level0 = [{ EmployeeID: 2, FirstName: "Andrew", ... }]; // 根节点
const level1 = [
  { EmployeeID: 8, FirstName: "L

aura", ReportsTo: 2 }, { EmployeeID: 1, FirstName: "Nancy", ReportsTo: 2 }, { EmployeeID: 5, FirstName: "Steven", ReportsTo: 2 } ]; const level2 = [ { EmployeeID: 6, FirstName: "Michael", ReportsTo: 5 }, { EmployeeID: 7, FirstName: "Robert", ReportsTo: 5 }, { EmployeeID: 9, FirstName: "Anne", ReportsTo: 5 } ]; // 合并为单层扁平数组(含 parentId) const allNodes = [...level0, ...level1, ...level2]; // 构建树:O(n) 时间复杂度 function buildTree(nodes, idKey = 'EmployeeID', parentKey = 'ReportsTo') { const nodeMap = new Map(); const roots = []; // 第一遍:建立 ID → 节点映射 nodes.forEach(node => { nodeMap.set(node[idKey], { ...node, children: [] }); }); // 第二遍:挂载子节点 nodes.forEach(node => { const parentId = node[parentKey]; const currentNode = nodeMap.get(node[idKey]); if (parentId == null || !nodeMap.has(parentId)) { roots.push(currentNode); } else { nodeMap.get(parentId).children.push(currentNode); } }); return roots; } const treeData = buildTree(allNodes); console.log(treeData); // 输出符合要求的嵌套结构
⚠️ 注意:若原始数据无 ReportsTo,需在请求时明确传递上下文(如 /api/employees?parent=2),或由前端维护映射表(如 parentMap = {2: [1,3,4,5,8], 5: [6,7,9]})。

✅ 方案二:按预设层级顺序手动组装(适用于固定结构)

若层级关系严格固定(如“第二组一定属于第一组首个元素”),可用简洁方式手动挂载:

// 假设 level0 仅含一个根节点
if (level0.length > 0) {
  level0[0].children = level1;
  // 手动查找 level1 中 EmployeeID === 5 的节点,并挂载 level2
  const managerNode = level1.find(emp => emp.EmployeeID === 5);
  if (managerNode) {
    managerNode.children = level2;
    managerNode.expanded = "true"; // 可选:控制默认展开
  }
}
// level0 即为最终树根
const finalTree = level0;

? 辅助函数:嵌套结构 ↔ 扁平数组互转

开发调试时,常需将嵌套树转为扁平列表(如用于搜索、导出):

// 嵌套 → 扁平(广度优先)
function nestedToLinear(data) {
  const result = [];
  const queue = [...data];

  while (queue.length > 0) {
    const node = queue.shift();
    result.push({ ...node, children: undefined }); // 移除 children 避免循环引用
    if (Array.isArray(node.children) && node.children.length > 0) {
      queue.push(...node.children);
    }
  }
  return result;
}

// 使用示例
console.log(nestedToLinear(treeData)); // 输出单层数组,含全部节点

✅ 总结

通过以上方法,你可灵活将任意数量的扁平数组组合为标准树形数据,无缝对接各类 TreeGrid 组件。