如何用JavaScript动态从对象数组创建多级深层嵌套对象?
实现动态多层嵌套对象生成方案
我明白你现在需要把扁平的数组转换成深层嵌套的结构,而且要支持动态的分组层级——从你的代码来看,之前的实现是生成扁平的分组结果,而不是深层嵌套的,这就是没法实现二级分组的核心原因。咱们来一步步重构逻辑,先从匹配你需求的具体场景入手,再扩展到通用的动态分组方案。
针对你需求的具体实现(group→brands→specs)
首先,你的期望结构是先按group分组,每个分组下是brands数组,每个品牌下再拆分specs为power(包含engines、brakes这类带av/tv的条目)和cost(包含成本相关信息)。用Lodash可以很简洁地实现这个逻辑:
const transformToNested = (data) => { // 1. 第一步:按最外层的group字段分组 const groupedByGroup = _.groupBy(data, 'group'); // 2. 转换每个group对应的内容 return _.map(groupedByGroup, (items, groupName) => { // 3. 在当前group下,按brandId分组(brandId是品牌的唯一标识) const groupedByBrand = _.groupBy(items, 'brandId'); // 4. 转换每个品牌的结构 const brands = _.map(groupedByBrand, (brandItems, brandId) => { // 提取品牌基础信息(同一品牌的信息一致,取第一个条目即可) const { brandName } = brandItems[0]; // 筛选并格式化power相关的规格(包含spec_desc、av、tv的条目) const powerSpecs = brandItems .filter(item => item.spec_desc && item.av !== undefined && item.tv !== undefined) .map(({ spec_desc, av, tv }) => ({ spec_desc, av, tv })); // 提取成本信息(同一品牌同一group下成本信息一致,取第一个匹配条目) const costItem = brandItems.find(item => item.cost !== undefined); const cost = costItem ? { desc: costItem.desc, value: costItem.cost, currency: costItem.currency } : undefined; // 组装品牌的最终结构 return { brandId: parseInt(brandId), brandName, specs: { ...(powerSpecs.length && { power: powerSpecs }), // 只有有数据时才添加power字段 ...(cost && { cost }) // 只有有成本数据时才添加cost字段 } }; }); // 提取group的id(同一group下id一致,取第一个条目) const { id } = items[0]; // 组装group的最终结构 return { group: groupName, id, brands }; }); }; // 测试用的实际输入数据 const actualData = [ { brandId: 1, spec_desc: "engines", av: 3000, tv: 1000, brandName: "bmw", id: 1, group: "cars", cost: 20000.00, currency: "USD", desc: "manufacturing costs" }, { brandId: 1, spec_desc: "brakes", av: 1000, tv: 2000, brandName: "bmw", id: 1, group: "cars" }, { brandId: 2, spec_desc: "engines", av: 1800, tv: 2500, brandName: "audi", id: 1, group: "cars" } ]; // 生成期望的嵌套结构 const desiredResult = transformToNested(actualData); console.log(desiredResult);
代码逻辑说明:
- 用
_.groupBy完成两层分组:先按group,再按brandId,确保同一组、同一品牌的条目都聚合在一起。 - 对于每个品牌,我们拆分出两种类型的数据:
power规格(过滤出带av/tv的条目)和cost信息(提取成本相关字段),避免冗余数据。 - 用对象扩展运算符
...来动态添加字段,只有当对应数据存在时才生成,保证结构简洁。
扩展:支持动态分组层级的通用方案
如果之后你需要更灵活的动态分组(比如新增category层级,或者调整分组顺序),可以写一个递归的通用分组函数,适配任意层级的分组需求:
// 通用递归分组函数:按传入的keys数组逐层分组 const nestByKeys = (data, keys) => { if (keys.length === 0) return data; const [currentKey, ...remainingKeys] = keys; const grouped = _.groupBy(data, currentKey); return _.map(grouped, (items, keyValue) => ({ [currentKey]: typeof keyValue === 'string' && !isNaN(keyValue) ? parseInt(keyValue) : keyValue, children: nestByKeys(items, remainingKeys) })); }; // 转换通用分组结果为你需要的业务结构 const transformWithDynamicNesting = (data, groupKeys) => { // 先按指定keys完成深层分组 const nestedGroups = nestByKeys(data, groupKeys); // 处理最底层的specs拆分逻辑 const processBrandSpecs = (brandItems) => { const powerSpecs = brandItems .filter(i => i.spec_desc && i.av && i.tv) .map(({ spec_desc, av, tv }) => ({ spec_desc, av, tv })); const costItem = brandItems.find(i => i.cost); const cost = costItem ? { desc: costItem.desc, value: costItem.cost, currency: costItem.currency } : null; // 提取品牌基础信息 const baseInfo = _.pick(brandItems[0], groupKeys.slice(1)); // 排除最外层的group字段 return { ...baseInfo, specs: { ...(powerSpecs.length && { power: powerSpecs }), ...(cost && { cost }) } }; }; // 递归转换通用嵌套结构为业务命名(比如children→brands) const processNestedStructure = (nodes) => { return nodes.map(node => { const currentKey = Object.keys(node)[0]; // 判断是否到了最底层分组(children是原始数据数组) if (Array.isArray(node.children[0])) { const brandData = processBrandSpecs(node.children); return { [currentKey]: node[currentKey], ...brandData }; } else { // 上层分组,继续递归,把children命名为下一层的复数形式(比如brand→brands) const nextKey = remainingKeys.length > 0 ? `${remainingKeys[0]}s` : 'brands'; return { [currentKey]: node[currentKey], [nextKey]: processNestedStructure(node.children) }; } }); }; return processNestedStructure(nestedGroups); }; // 使用示例:按['group', 'brandId']动态分组 const dynamicResult = transformWithDynamicNesting(actualData, ['group', 'brandId']); console.log(dynamicResult);
通用方案说明:
nestByKeys是核心递归函数,接受一个分组keys数组,生成通用的嵌套结构(用children存放子节点)。transformWithDynamicNesting把通用结构转换成你业务需要的命名(比如把children改成brands),同时处理specs的拆分逻辑。- 如果之后需要调整分组层级,只需要修改
groupKeys数组即可(比如改成['group', 'category', 'brandId'])。
对比你原有代码的改进点
你之前的grouparray函数是把分组结果扁平化处理(每个分组层级都作为同级字段),而我们的方案是构建深层嵌套的结构,这正是实现多级分组的关键。通过递归或者逐层分组+聚合的方式,就能轻松实现任意层级的嵌套。
内容的提问来源于stack exchange,提问作者Dev
相关产品推荐
相关产品推荐

