/**
* 数组对象按指定属性分组,合并金额
* @param {Array} arr - 原始数组(元素为对象)
* @param {string} groupKey - 分组依据的属性名(如 'type'、'id')
* @param {string} moneyKey - 需合并的金额属性名(如 'money'、'amount')
* @returns {Array} 合并后的数组
*/
function mergeObjectsByKey(arr, groupKey, moneyKey) {
// 用 Map 分组(比普通对象更安全,支持任意类型键)
const resultMap = new Map();
arr.forEach(item => {
// 1. 获取当前 item 的分组键值(如 item.type = 'A')
const key = item[groupKey];
// 2. 确保金额为数字(兼容字符串格式的金额,如 '100' → 100)
const currentMoney = Number(item[moneyKey]) || 0;
// 3. 检查 Map 中是否已存在该分组
if (resultMap.has(key)) {
// 存在:累加金额
const existingItem = resultMap.get(key);
existingItem[moneyKey] += currentMoney;
} else {
// 不存在:创建新分组对象(深拷贝避免修改原数据)
const newItem = { ...item }; // 复制原对象所有属性
newItem[moneyKey] = currentMoney; // 初始化金额(确保为数字)
resultMap.set(key, newItem);
}
});
// 4. 将 Map 的值转为数组返回
return Array.from(resultMap.values());
}
场景:按 type 分组,合并 money 金额
// 原始数组
const originalArr = [
{ id: 1, type: '水果', name: '苹果', money: 10 },
{ id: 2, type: '蔬菜', name: '白菜', money: 5 },
{ id: 3, type: '水果', name: '香蕉', money: 15 }, // 同 type='水果'
{ id: 4, type: '肉类', name: '猪肉', money: 30 },
{ id: 5, type: '蔬菜', name: '萝卜', money: '8' }, // 金额为字符串格式
];
// 调用函数:按 type 分组,合并 money
const mergedArr = mergeObjectsByKey(originalArr, 'type', 'money');
console.log(mergedArr);
输出结果(按 type 分组,金额累加):
[
{ id: 1, type: '水果', name: '苹果', money: 25 }, // 10 + 15 = 25
{ id: 2, type: '蔬菜', name: '白菜', money: 13 }, // 5 + 8 = 13(字符串自动转数字)
{ id: 4, type: '肉类', name: '猪肉', money: 30 }
]
关键说明
分组逻辑:用 Map 存储分组,键为 groupKey 的值(如 ‘水果’),值为合并后的对象。Map 比普通对象更安全(避免键名冲突,支持数字 / 对象等类型的键)。
金额兼容:通过 Number(item[moneyKey]) || 0 处理:
数字金额(如 10)直接使用;
字符串金额(如 ‘8’)自动转为数字;
缺失金额或非数字(如 undefined、‘abc’)视为 0。
属性保留:用 { …item } 浅拷贝原对象的所有属性(如 id、name),合并时仅更新金额,其他属性保留第一个分组对象的值(若需保留所有属性,可修改为数组存储,如 name: [‘苹果’, ‘香蕉’])。
扩展需求:保留分组内所有非金额属性
如果需要保留同组所有对象的非金额属性(如合并 name 为数组),可修改函数:
function mergeObjectsWithAllProps(arr, groupKey, moneyKey) {
const resultMap = new Map();
arr.forEach(item => {
const key = item[groupKey];
const currentMoney = Number(item[moneyKey]) || 0;
if (resultMap.has(key)) {
const existing = resultMap.get(key);
existing[moneyKey] += currentMoney;
// 非金额属性转为数组(去重)
Object.keys(item).forEach(prop => {
if (prop !== groupKey && prop !== moneyKey) {
if (!existing[prop].includes(item[prop])) {
existing[prop].push(item[prop]);
}
}
});
} else {
const newItem = { [moneyKey]: currentMoney };
// 分组键保留原值,其他属性转为数组
Object.keys(item).forEach(prop => {
newItem[prop] = prop === groupKey ? item[prop] : [item[prop]];
});
resultMap.set(key, newItem);
}
});
return Array.from(resultMap.values());
}
扩展示例输出:
[
{
type: '水果',
id: [1, 3],
name: ['苹果', '香蕉'],
money: 25
},
// ... 其他分组
]```
边界情况处理
原始数组为空:返回空数组;
部分对象缺失 groupKey:按 undefined 分组;
金额为 0 或负数:正常累加(符合业务逻辑);
分组键为复杂类型(如对象):Map 可直接支持(普通对象会转为字符串 [object Object],不推荐用对象作为分组键)。
640

被折叠的 条评论
为什么被折叠?



