如何高效地将嵌套对象数组扁平化为键值映射数组

本文介绍一种使用 map 与对象遍历结合的方式,将含 items 数组(每个 item 是 { [key]: string, quantity: number } 结构)的原始对象数组,转换为每个对象直接以产品名为属性、数量为值的新数组。

在实际开发中,我们常遇到数据结构嵌套较深的场景:例如订单列表中每个订单包含多个商品项(items),而每项又以动态字段名(如 productOne、productTwo)标识商品类型,quantity 表示数量。此时若需将 items “展开”为同级属性(如 crust: 2.2, dust: 34),就需要一次语义清晰、可维护性强的扁平化处理。

核心思路是:对原数组每一项执行 map,创建新对象副本 → 遍历其 items 数组 → 对每个 item 动态提取非 quantity 字段作为属性名,quantity 值作为对应属性值 → 最后删除 items 字段,返回精简后的对象。

以下是推荐实现(已优化可读性与健壮性):

function flattenItems(arr) {
  return arr.map(item => {
    // 浅拷贝原对象,避免污染源数据
    const flattened = { ...item };

    // 遍历 items 数组
    for (const entry of item.items || []) {
      // 遍历 entry 的每个键,识别 product 字段和 quantity
      let productName = null;
      let quantityValue = null;

      for (const key in entry) {
        if (key === 'quantity') {
          quantityValue = entry[key];
        } else if (typeof entry[key] === 'string') {
          productName = entry[key];
        }
      }

      // 安全赋值:仅当两者均存在时才挂载到结果对象
      if (productName !== null && quantityValue !== undefined) {
        flattened[productName] = quantityValue;
      }
    }

    // 移除原始 items 字段
    delete flattened.items;

    return flattened;
  });
}

// 示例输入
const firstArray = [
  {
    amount: 343,
    code: "RCU8YI0NKS",
    items: [
      { productOne: "crust", quantity: 2.2 },
      { productTwo: "dust", quantity: 34 }
    ],
    user_id: "wewewefwOHG22323kj"
  },
  {
    amount: 343,
    code: "RCU8YI0NKS",
    items: [
      { productTwo: "dust", quantity: 32 },
      { productThree: "must", quantity: 34 }
    ],
    user_id: "m2LgLRD9MEVNAX56JTpRYDkOOjd2"
  }
];

console.log(flattenItems(firstArray));
// 输出即为目标格式 secondArray

注意事项

  • 该方法时间复杂度为 O(n × m)(n 为外层数组长度,m 为单个 items 平均长度),属最优解——因必须访问每个 item 才能提取键值,无可避免;
  • 使用 ...item 浅拷贝更符合现代 JS 实践,比 Object.assign({}, item) 更简洁;
  • 显式检查 item.items || [] 和 typeof entry[key] === 'string' 可防御空值或异常结构;
  • 若存在多个 product 字段(如同时有 productOne 和 productTwo),当前逻辑取第一个非 quantity 字符串值作为 product name —— 若业务要求更精确匹配(如只认 product.* 开头的 key),可替换为正则校验:/^(product|item)/i.test(key);
  • 若需支持重复 product 名合并(如多个 dust 合并为总量),可在赋值前做累加判断。

此方案简洁、可读、可扩展,适用于中后台数据聚合、报表生成及 API 响应适配等典型场景。