JavaScript中合并含多值键的两个对象数组的更优方案?
问题描述
现有两个对象数组:
const students = [ { student_id: 1, name: "Arthur" }, { student_id: 2, name: "Peter" }, { student_id: 3, name: "Molly" }, ]; const student_objects = [ { student_id: 1, object: "pen" }, { student_id: 2, object: "pen" }, { student_id: 3, object: "book" }, { student_id: 1, object: "book" }, { student_id: 3, object: "phone" }, ];
预期输出:
[ { student_id: 1, name: 'Arthur', object: [ 'pen', 'book' ] }, { student_id: 2, name: 'Peter', object: [ 'pen' ] }, { student_id: 3, name: 'Molly', object: [ 'book', 'phone' ] } ]
我尝试了两种实现方法:
第一种:
function practice(students, student_objects) { students.forEach((ele) => (ele.object = [])); for (let i = 0; i < students.length; i++) { for (let j = 0; j < student_objects.length; j++) { if (student_objects[j].student_id === students[i].student_id) { students[i].object.push(student_objects[j].object); } } } return students; }
第二种:
function practice(students, student_objects) { const student_objects_new = student_objects.reduce((obj, item) => { obj[item.student_id] = obj[item.student_id] || []; obj[item.student_id].push(item.object); return obj; }, {}); const answer = students.map((element) => ({ ...element, object: [...student_objects_new[element.student_id]], })); return answer; }
但这两种方法效率都不高,请问是否有更优的实现方式?
更优实现方案
先明确:你的第二种方法已经达到了**O(n+m)**的线性时间复杂度(n为students长度,m为student_objects长度),这是理论上的最优复杂度——毕竟要完成需求,两个数组至少得各遍历一次。可以通过优化细节进一步提升实际运行效率:
优化版实现1(不修改原数组,性能更优)
function practice(students, student_objects) { // 预处理物品数据,建立ID到物品数组的映射 const objMap = {}; for (const item of student_objects) { // 逻辑空赋值:仅当对应ID无数据时初始化数组,避免覆盖已有值 objMap[item.student_id] ??= []; objMap[item.student_id].push(item.object); } // 遍历学生数组合并数据,避免不必要的数组拷贝 return students.map(student => ({ ...student, object: objMap[student.student_id] || [] })); }
优化点说明
- 用
for...of替代reduce:循环逻辑更直观,在多数JS引擎中性能略优于reduce; - 用
??=替代||赋值:更严谨——如果某ID对应的物品数组为空,||会错误地重新初始化,而??=仅在值为null/undefined时生效; - 去掉不必要的数组展开:原代码中
[...student_objects_new[element.student_id]]会额外创建数组副本,若不需要独立的数组引用,直接复用映射表中的数组即可,减少内存开销。
优化版实现2(允许修改原数组,性能最大化)
如果不需要保留原students数组的纯净性,可以直接在原对象上添加属性,避免创建新对象的内存开销:
function practice(students, student_objects) { const objMap = {}; for (const item of student_objects) { objMap[item.student_id] ??= []; objMap[item.student_id].push(item.object); } for (const student of students) { student.object = objMap[student.student_id] || []; } return students; }
这种写法的运行效率是最高的,因为没有额外的对象和数组拷贝操作,仅做属性赋值和遍历。
内容的提问来源于stack exchange,提问作者hippoPandamas
相关产品推荐
相关产品推荐

