JavaScript 根据数组对象的某个 指定属性 分组合并,并累加金额

/**
 * 数组对象按指定属性分组,合并金额
 * @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],不推荐用对象作为分组键)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值