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










