
本文介绍如何将一维对象数组依据预设的层级顺序(如 soc → hlgt → hlt → pt → llt)递归分组,生成符合业务语义的嵌套树形数据结构,适用于医学术语分类、导航菜单、多级筛选等场景。
本文介绍如何将一维对象数组依据预设的层级顺序(如 soc → hlgt → hlt → pt → llt)递归分组,生成符合业务语义的嵌套树形数据结构,适用于医学术语分类、导航菜单、多级筛选等场景。
在实际开发中,我们常遇到将扁平的术语列表(如 MedDRA 术语体系)转换为层级清晰的树形结构的需求。例如,一个包含 SOC(System Organ Class)、HLGT(High Level Group Term)、HLT(High Level Term)、PT(Preferred Term)和 LLT(Lowest Level Term)字段的对象数组,需按从粗到细的语义层级(即 SOC 为根,LLT 为叶)组织成嵌套树。这种“自顶向下、逐层分组”的过程,本质上是逆向树构建(reverse tree building)——即以原始数据的高阶分类字段为父节点,逐步向下聚合子项。
实现的核心逻辑是递归分组:每次取当前层级键(如 'SOC'),使用 Object.groupBy() 按该键值对数据分组;每组生成一个节点 { key, value, children },其中 children 是对本组数据在剩余键序列上递归调用的结果。当键列表为空时,返回空数组(叶子节点无子节点)。
以下是完整、可直接运行的实现代码:
const groupBy = (data, keys) => {
if (!keys.length) return [];
const key = keys[0];
const getValue = (obj) => obj[key];
return Object.entries(Object.groupBy(data, getValue))
.map(([value, items]) => ({
key,
value,
children: groupBy(items, keys.slice(1))
}));
};
// 示例数据(已精简,实际使用时替换为完整数组)
const data = [
{ SOC: "Endocrine disorders", HLGT: "Adrenal gland disorders", HLT: "Adrenal gland disorders NEC", PT: "Adrenal mass", LLT: "Adrenal mass" },
{ SOC: "Endocrine disorders", HLGT: "Adrenal gland disorders", HLT: "Adrenal cortical hypofunctions", PT: "Adrenal insufficiency", LLT: "Adrenal hypofunction" },
{ SOC: "Infections and infestations", HLGT: "Infections - pathogen unspecified", HLT: "Infections NEC", PT: "Adrenalitis", LLT: "Adrenalitis" }
];
// 定义层级顺序(从根到叶)
const keys = ['SOC', 'HLGT', 'HLT', 'PT', 'LLT'];
const tree = groupBy(data, keys);
console.log(JSON.stringify(tree, null, 2));
✅ 关键特性说明:
-
语义清晰:每个节点明确携带
key(字段名)与value(该层级具体值),便于渲染时显示标题或作为筛选条件; -
结构健壮:自动处理重复值、空值及层级缺失(如某条数据缺少
HLGT字段时,Object.groupBy会将其归入undefined组,可根据需要过滤); -
可扩展性强:只需修改
keys数组即可适配任意层级结构(如仅需三级:['Category', 'Subcategory', 'Item'])。
⚠️ 注意事项:
-
Object.groupBy()是 ECMAScript 2024 新增方法,现代浏览器(Chrome 117+、Firefox 120+、Safari 17.4+)已支持;若需兼容旧环境,请使用reduce()手动实现分组逻辑; - 若原始数据存在大小写混用或前后空格,建议在分组前统一标准化(如
getValue: obj => String(obj[key] || '').trim().toLowerCase()); - 对于超大数据集(>10,000 条),可考虑添加节流或 Web Worker 异步处理,避免阻塞主线程。
最终生成的树结构天然支持前端组件消费,例如在 Vue/React 中递归渲染 <treenode></treenode>,或用于 Ant Design 的 Tree 组件 treeData 属性。掌握这一模式,即可高效应对各类“扁平转树形”的通用数据建模需求。










