
本文介绍如何对具有嵌套 children 数组的树形对象数组,进行全局深度优先排序——即先按最大嵌套深度降序排列,深度相同时再按直接子节点数量降序排列,并递归应用至每一层。
本文介绍如何对具有嵌套 children 数组的树形对象数组,进行全局深度优先排序——即先按最大嵌套深度降序排列,深度相同时再按直接子节点数量降序排列,并递归应用至每一层。
在构建组织架构、菜单导航、文件目录或任务依赖图等树形数据结构时,常需按“结构复杂度”对节点排序:深层嵌套的分支往往语义上更“细化”或“具体”,应优先展示;若深度相同,则子项更多者通常更具展开价值。这种排序不能仅靠单层 sort() 实现,而需自底向上计算每个节点的最大深度(max depth),再自顶向下稳定排序并清理临时字段。
核心思路:两阶段递归处理
-
深度标注阶段(setDepth):
为每个节点注入 depth 属性,表示其子树的最大嵌套深度(即从该节点出发,沿任意 children 路径能到达的最远层级数)。注意:叶子节点(children: [])深度为 0;仅含空数组的节点深度为 0;有子节点的节点深度 = 1 + 子树最大深度。 -
排序与清理阶段(sort):
对当前层级所有节点按 (b.depth – a.depth) 降序(深度大者靠前),深度相同时按 (b.children.Length – a.children.length) 降序(子节点多者靠前);随后递归排序每个节点的 children 数组,最后删除临时 depth 字段,保持数据纯净。
完整可运行代码示例
const treeData = [{ value: '1', children: [ { value: '1.1', children: [{ value: '1.1.1', children: [] }] }, { value: '1.2', children: [{ value: '1.2.1', children: [ { value: '1.2.1.1', children: [] }, { value: '1.2.1.2', children: [] } ] }] }, { value: '1.3', children: [{ value: '1.3.1', children: [{ value: '1.3.1.1', children: [{ value: '1.3.1.1.1', children: [] }] }] }] } ] }]; // 步骤1:递归计算每个节点的子树最大深度 function setDepth(nodes, depth = 0) { if (!Array.isArray(nodes) || nodes.length === 0) return 0; // 为每个子节点计算其子树深度,并赋值给 child.depth nodes.forEach(child => { child.depth = setDepth(child.children); }); // 当前层级最大深度 = 所有子节点 depth 的最大值 + 1(自身到子节点这一层) const maxChildDepth = Math.max(...nodes.map(child => child.depth), 0); return maxChildDepth + 1; } // 步骤2:递归排序(深度优先 + 同深比子量)并清理 depth 字段 function sortTree(nodes) { if (!Array.isArray(nodes)) return; // 主排序:深度降序 → 子节点数降序 nodes.sort((a, b) => (b.depth ?? 0) - (a.depth ?? 0) || b.children.length - a.children.length); // 递归排序每个子节点的 children nodes.forEach(node => { sortTree(node.children); delete node.depth; // 清理临时字段,保持原始结构干净 }); } // 执行流程 setDepth(treeData); sortTree(treeData); console.log(JSON.stringify(treeData, null, 2)); // 输出结果中,'1.3'(深度4)排第一,'1.2'(深度3)次之,'1.1'(深度2)最后 —— 符合预期
关键注意事项
- ✅ 深度定义明确:depth 表示“以该节点为根的子树最大层数”,非节点自身所在层级(即不计算父链)。例如 ‘1.3.1.1.1’ 是叶子,其 depth = 0;其父节点 ‘1.3.1.1’ 的 depth = 1。
- ✅ 稳定性保障:排序使用 || 短路逻辑,确保深度相同时严格按 children.length 排序;递归调用保证每层独立有序。
- ⚠️ 避免副作用:setDepth 直接修改原对象。如需不可变操作,请先深拷贝(如 structuredClone(treeData))。
- ⚠️ 边界防护:代码已加入 Array.isArray() 和空数组判断,防止 children: undefined 或非数组值导致运行时错误。
- ? 扩展提示:若需升序(浅层优先),将排序比较函数中的 b.depth – a.depth 改为 a.depth – b.depth 即可。
该方案时间复杂度为 O(N)(两次遍历,N 为总节点数),空间复杂度为 O(H)(递归栈深度 H),适用于中大型树形结构,是生产环境中兼顾可读性与性能的稳健实践。