
本文介绍如何将具有层级语义的扁平对象数组(如医学术语编码数据),按指定字段顺序(如 soc → hlgt → hlt → pt → llt)递归分组,构建自顶向下、键值明确、支持无限嵌套的树形结构。
本文介绍如何将具有层级语义的扁平对象数组(如医学术语编码数据),按指定字段顺序(如 soc → hlgt → hlt → pt → llt)递归分组,构建自顶向下、键值明确、支持无限嵌套的树形结构。
在医疗、药学或分类系统(如 MedDRA)中,常存在一组扁平的对象,每个对象包含多个语义层级字段(例如 SOC(系统器官分类)、HLGT(高位组术语)、HLT(高位术语)、PT(首选术语)、LLT(低位术语))。这些字段天然构成一棵从粗到细的正向层级树,但实际开发中常需将其转换为反向树结构(reverse tree)——即以最顶层分类(如 SOC)为根,逐级向下展开子节点,最终形成符合 UI 渲染(如 TreeView 组件)或 API 响应规范的嵌套 JSON。
核心思路是:按预定义字段顺序依次分组,每层提取唯一键值,将同键下的子集递归处理为 children 数组。该过程无需预先建模节点 ID 或 parent 引用,完全基于字段值聚合,简洁且可扩展。
以下为完整实现:
const groupBy = (data, keys) => {
// 递归终止:无剩余字段时返回空数组(叶子节点无 children)
if (keys.length === 0) return [];
const key = keys[0];
// 安全取值函数,避免 undefined 导致分组异常
const getValue = (obj) => obj?.[key] ?? 'N/A';
// 使用 Object.groupBy(ES2024)按当前 key 分组;若环境不支持,可用 reduce 模拟
return Object.entries(Object.groupBy(data, getValue))
.map(([value, items]) => ({
key, // 当前层级字段名(如 'SOC')
value, // 当前层级字段值(如 'Endocrine disorders')
children: groupBy(items, keys.slice(1)) // 递归处理剩余字段
}));
};
// 示例数据与层级顺序(从顶到底)
const data = [/* ... 48 条医学术语对象,详见原始输入 */];
const keys = ['SOC', 'HLGT', 'HLT', 'PT', 'LLT'];
const tree = groupBy(data, keys);
console.log(JSON.stringify(tree, null, 2));
✅ 关键特性说明:
-
严格层级对齐:
keys数组顺序即树的深度顺序,确保SOC总是根级,LLT为最深层叶子(即使某条数据缺失中间字段,也会被归入'N/A'组,便于调试); -
结构标准化:每个节点统一含
key(字段名)、value(字段值)、children(子树数组),便于前端统一渲染或后端序列化; -
零依赖 & 高兼容:仅使用原生
Object.groupBy(现代浏览器/Node.js ≥18.17),如需兼容旧环境,可替换为data.reduce()实现等效逻辑; -
可中断与定制:可在递归中加入过滤(如跳过空值)、去重(如合并相同
PT下的多个LLT)、或注入元信息(如count: items.length)。
⚠️ 注意事项:
- 若原始数据存在字段值为
null/undefined,建议在getValue中统一兜底(如示例中的'N/A'),避免分组失败; -
Object.groupBy对Symbol或非字符串键支持有限,确保所有key字段值可安全转为字符串; - 对超大数据集(>10k 条),可考虑增加节流或 Web Worker 卸载计算,避免阻塞主线程。
该方法本质是“声明式分组树构建”,将复杂的嵌套逻辑封装为纯函数,一次调用即可生成符合业务语义的逆向层级树,大幅提升分类数据的可视化与交互能力。










