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);
方案优势
- 避免引用问题:所有分组结果都是新创建的对象,不会修改原数组的任何元素,确保每次执行代码都基于原始数据计算
- 性能更优:使用对象映射查找的时间复杂度为O(1),比原代码中
findIndex的O(n)更高效 - 逻辑清晰:中间映射的方式更直观,便于维护和扩展
内容的提问来源于stack exchange,提问作者Davide la Fogia
相关产品推荐
相关产品推荐

