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

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] || []
  }));
}

优化点说明

  1. 用for...of替代reduce:循环逻辑更直观,在多数JS引擎中性能略优于reduce;
  2. 用??=替代||赋值:更严谨——如果某ID对应的物品数组为空,||会错误地重新初始化,而??=仅在值为null/undefined时生效;
  3. 去掉不必要的数组展开:原代码中[...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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 13:54:38