
本文介绍一种使用 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 响应适配等典型场景。