JavaScript中从对象数组生成通用n级嵌套层级结构的实现方案
扁平对象数组转n级层级结构的通用解决方案
问题背景
给定如下扁平结构的对象数组:
[ {"company": "Google", "country": "USA", "employee": "John"}, {"company": "Amazon", "country": "UK", "employee": "Arya"}, {"company": "Google", "country": "KSA", "employee": "Cersi"}, {"company": "Amazon", "country": "USA", "employee": "Tyrion"}, {"company": "Amazon", "country": "USA", "employee": "Daenarys"}, {"company": "Google", "country": "KSA", "employee": "Dothrokhi"} ]
需要将其转换为指定的层级结构:
{ "company": [ { "company": "Amazon", "country": [ { "country": "UK", "employee": [ "Arya" ] }, { "country": "USA", "employee": [ "Tyrion", "Daenarys" ] } ] }, { "company": "Google", "country": [ { "country": "KSA", "employee": [ "Cersi", "Dothrokhi" ] }, { "country": "USA", "employee": [ "John" ] } ] } ] }
核心疑问
- 使用
array.reduce()或array.map()哪种方案更优? - 是否有其他更合适的实现方法?
- 如何实现通用n级树形结构(支持
level1->level2->…->leveln的层级转换),同时处理重复数据?
现有非通用实现(仅支持3级)
以下代码仅能处理示例中的3级结构,无法适配任意层级:
const data = [{ company: "Google", country: "USA", employee: "John" }, { company: "Amazon", country: "UK", employee: "Arya" }, { company: "Google", country: "KSA", employee: "Cersi" }, { company: "Amazon", country: "USA", employee: "Tyrion" }, { company: "Amazon", country: "USA", employee: "Daenarys" }, { company: "Google", country: "KSA", employee: "Dothrokhi" } ]; let res = Object.entries( data.reduce((result, { company, country, employee }) => { const groupedSubtypeList = (result[company] ??= []); if (country !== null) { groupedSubtypeList.push({ country, employee }); } return result; }, {}) ) .map(([company, countries]) => { const companyItem = { company: company }; if (countries.length >= 1) { let temp = Object.entries( countries.reduce((result, { country, employee }) => { const groupedSubtypeList = (result[country] ??= []); if (employee !== null) { groupedSubtypeList.push(employee); } return result; }, {}) ).map(([country, employee]) => { const countryItem = { country: country }; if (employee.length >= 1) { countryItem.employee = employee.map((employee) => employee); } return countryItem; }); companyItem.country = temp; } return companyItem; }); console.log(res);
通用解决方案实现
方案选择:优先使用reduce
map仅能做一对一的转换,无法处理聚合分组逻辑;而reduce天生适合数据聚合、分组和层级构建,是这类场景的最优选择。我们可以基于reduce实现递归式的通用层级构建函数。
通用n级层级转换函数
以下函数支持传入任意层级的字段列表(如['company', 'country', 'employee']),自动构建对应的层级结构:
function buildHierarchy(data, levels) { // 递归构建层级的核心函数 function groupByLevel(items, levelIndex) { if (levelIndex >= levels.length - 1) { // 最后一级,返回去重后的数值数组 return [...new Set(items.map(item => item[levels[levelIndex]]))]; } const currentLevel = levels[levelIndex]; const nextLevel = levels[levelIndex + 1]; // 使用reduce分组当前层级的所有值 const grouped = items.reduce((acc, item) => { const key = item[currentLevel]; if (!acc[key]) { acc[key] = []; } acc[key].push(item); return acc; }, {}); // 转换为目标格式:[{ [currentLevel]: key, [nextLevel]: 子层级数据 }] return Object.entries(grouped).map(([key, childItems]) => ({ [currentLevel]: key, [nextLevel]: groupByLevel(childItems, levelIndex + 1) })); } // 最外层包装:{ [第一级字段名]: 层级数据 } const rootLevel = levels[0]; return { [rootLevel]: groupByLevel(data, 0) }; } // 使用示例 const data = [ {"company": "Google", "country": "USA", "employee": "John"}, {"company": "Amazon", "country": "UK", "employee": "Arya"}, {"company": "Google", "country": "KSA", "employee": "Cersi"}, {"company": "Amazon", "country": "USA", "employee": "Tyrion"}, {"company": "Amazon", "country": "USA", "employee": "Daenarys"}, {"company": "Google", "country": "KSA", "employee": "Dothrokhi"} ]; // 传入层级字段列表 const hierarchy = buildHierarchy(data, ['company', 'country', 'employee']); console.log(JSON.stringify(hierarchy, null, 2));
代码说明
- 递归分组:通过
groupByLevel递归处理每一层级,直到最后一级时返回去重后的数值数组。 - 自动适配层级:只需传入不同的层级字段列表(如
['department', 'team', 'role', 'name']),即可生成对应n级结构。 - 去重处理:最后一级使用
new Set自动去重,若中间层级需要去重,可在分组时增加判断逻辑。 - 灵活扩展:如果需要调整层级格式(如修改子节点字段名),只需修改递归返回的对象结构即可。
其他可选方案
- 迭代式实现:对于层级极深的场景,递归可能存在栈溢出风险,可将递归改为循环迭代的方式,逐个层级构建。
- 第三方工具函数:如
lodash.groupBy可以简化单层级分组,但多层级仍需自行组合逻辑,不如自定义函数灵活。
内容的提问来源于stack exchange,提问作者JEEVAN GEORGE ANTONY
相关产品推荐
相关产品推荐

