You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript数组对象分组求和实现及累加异常问题求助

问题:按项目分组并累加小时数

需求

  • 对给定的对象数组按项目(通过项目ID唯一标识)分组
  • 同一项目的hours字段进行求和
  • 最终输出仅包含项目名称和总小时数的数组

原始数组

const projects = [
    {
      "project": { "id": 1, "name": "Mars Rover" },
      "employee": { "id": 1, "name": "Mario" },
      "date": "2021-08-26T22:00:00.000Z",
      "hours": 5
    },
    {
      "project": { "id": 2, "name": "Manhattan" },
      "employee": { "id": 2, "name": "Giovanni" },
      "date": "2021-08-30T22:00:00.000Z",
      "hours": 3
    },
    {
      "project": { "id": 1, "name": "Mars Rover" },
      "employee": { "id": 1, "name": "Mario" },
      "date": "2021-08-31T22:00:00.000Z",
      "hours": 3
    },
    {
      "project": { "id": 1, "name": "Mars Rover" },
      "employee": { "id": 3, "name": "Lucia" },
      "date": "2021-08-31T22:00:00.000Z",
      "hours": 3
    },
    {
      "project": { "id": 2, "name": "Manhattan" },
      "employee": { "id": 1, "name": "Mario" },
      "date": "2021-08-26T22:00:00.000Z",
      "hours": 2
    },
    {
      "project": { "id": 2, "name": "Manhattan" },
      "employee": { "id": 2, "name": "Giovanni" },
      "date": "2021-08-31T22:00:00.000Z",
      "hours": 4
    }
];

期望输出

const projects = [
  {project: "Mars Rover", hours: 11},
  {project: "Manhattan", hours: 9}
];

现有实现及问题

现有代码

let newRows = [];

projects.forEach((element) => {
  if (newRows.length === 0) {
    newRows.push(element);
  } else {
    const index = newRows.findIndex(
      (project) => project.project.id === element.project.id
    );
    console.log(index);
    if (index > -1) {
      newRows[index].hours += element.hours;
    } else {
      newRows.push(element);
    }
  }
});

问题分析

这段代码逻辑上能实现需求,但存在重复累加的bug:每次修改文件触发热重载时,页面显示的总小时数会持续增加,刷新页面后才恢复正常。

原因是:代码直接将原数组的element引用推入newRows,后续修改newRows[index].hours时,实际上修改的是原数组对应元素的hours值。在热重载场景下,原数组会保留之前修改后的hours值,导致再次执行代码时,基于已被修改的数据重复累加,最终数值异常。

解决方案

修正后的代码

使用中间映射对象存储分组结果,全程操作新创建的对象,避免修改原数组元素:

// 用项目ID作为key,存储项目名称和累计小时数
const projectMap = {};

projects.forEach(element => {
  const { project: { id: projectId, name: projectName }, hours } = element;
  
  if (projectMap[projectId]) {
    // 项目已存在,累加小时数
    projectMap[projectId].hours += hours;
  } else {
    // 项目不存在,初始化新对象
    projectMap[projectId] = {
      project: projectName,
      hours: hours
    };
  }
});

// 将映射对象转换为目标数组格式
const newRows = Object.values(projectMap);

方案优势

  1. 避免引用问题:所有分组结果都是新创建的对象,不会修改原数组的任何元素,确保每次执行代码都基于原始数据计算
  2. 性能更优:使用对象映射查找的时间复杂度为O(1),比原代码中findIndex的O(n)更高效
  3. 逻辑清晰:中间映射的方式更直观,便于维护和扩展

内容的提问来源于stack exchange,提问作者Davide la Fogia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:45:04