You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:23:00