如何将字符串二维数组转换为嵌套对象数组(树形结构)

10次阅读

如何将字符串二维数组转换为嵌套对象数组(树形结构)

本文介绍在 next.js 13 管理后台开发中,如何使用 es6+ 原生 javascript 将扁平的字符串二维数组(含父子关系标识)高效构建为具有 `children` 层级结构的对象数组,适用于菜单、分类、组织架构等树形数据渲染场景。

在构建管理后台的侧边栏菜单、产品分类或组织架构树时,后端常返回扁平化的二维数组(如 [parentID, id, name]),而前端组件(如 Ant Design 的 Tree、MUI 的 TreeView 或自定义递归组件)需要的是嵌套的树形对象结构。直接使用 map() 仅能做线性映射,无法建立父子引用关系——这正是问题的核心难点。

解决思路是:先建立全局 ID → 节点对象的映射表(Map),再按父子关系动态挂载 children,最后筛选出根节点(parentID === “”)作为顶层入口。该方法时间复杂度为 O(n),无需递归,稳定高效。

以下是完整实现:

function buildTreeFromFlatArray(data: string[][]): Array<{ id: number; name: string; children: any[] }> {   const map = new Map();    // 第一步:遍历所有项,初始化每个节点(id 为 key),并存入 map   for (const [parentId, id, name] of data) {     map.set(id, {       id: parseInt(id, 10),       name,       children: [],     });   }    // 第二步:建立父子关系 —— 将子节点 push 到对应父节点的 children 中   for (const [parentId, id, name] of data) {     if (parentId !== "") {       const parent = map.get(parentId);       const child = map.get(id);       if (parent && child) {         parent.children.push(child);       }     }   }    // 第三步:收集所有根节点(parentId 为空字符串)   const roots: typeof map.values extends () => infer T ? T extends Iterable ? U[] : [] : [] : [] = [];   for (const [parentId] of data) {     if (parentId === "") {       const root = map.get(data.find(row => row[0] === "")?.[1] || "");       if (root) roots.push(root);     }   }    // 更健壮的根节点提取方式(推荐):   const result: typeof roots = [];   for (const row of data) {     if (row[0] === "") {       const root = map.get(row[1]);       if (root) result.push(root);     }   }    return result; }

关键要点说明:

  • 使用 Map 而非普通对象,避免字符串 id(如 “01”)被误转为数字键导致冲突;
  • parseInt(id, 10) 显式指定十进制,防止前导零引发八进制解析错误;
  • 依赖输入顺序:父节点必须出现在其子节点之前(如 [“”, “1”, “…”] 需在 [“1”, “2”, “…”] 之前)。若顺序不可控,需两轮遍历(第一轮建节点,第二轮补 children),本实现已满足该前提;
  • children 初始化为空数组,确保类型安全,便于后续 push 操作;
  • 最终返回的是独立的根节点数组,每个根及其全部后代均为引用关系,结构完整可直接用于 react 渲染。

? 扩展建议(Next.js 场景):
在 getServerSideProps 或 generateStaticParams 中调用此函数预处理数据,避免客户端重复计算;若数据量极大,可配合 useMemo 缓存结果;如需支持多层级深度限制或循环检测,可在第二步加入 visited 标记逻辑。

该方案简洁、可读性强、无外部依赖,完美适配现代 typescript/Next.js 工程实践。

text=ZqhQzanResources