
本文介绍如何将嵌套的二维用户数据数组,按 `class_id` 字段归并为统一结构:每个班级对应一个对象,其 `students` 数组包含该班所有学生(仅保留 `roll_number` 和 `name`)。核心方法是先扁平化再用 `reduce` 构建哈希映射,最后提取值。
在 Node.js(或现代浏览器环境)中处理多维嵌套数据时,直接使用 map 或 for 循环逐层遍历容易遗漏跨子数组的同字段聚合逻辑——正如提问中所示:原始数据是二维数组(每个子数组可能含多个不同 class_id 的学生),而需求是按 class_id 全局分组,而非按子数组顺序分组。
✅ 正确思路:扁平化 + 累积映射 + 提取值
- flat() 扁平化:将二维数组转为一维,消除嵌套层级干扰;
- reduce() 构建索引对象:以 class_id 为键,动态初始化班级对象 { class_id, students: [] };
- Object.values() 提取结果:将键值对映射转为符合要求的数组格式。
以下是完整、可直接运行的解决方案:
const data = [
[
{ class_id: "Grade 1", roll_number: "1", name: "alex" },
{ class_id: "Grade 1", roll_number: "2", name: "bob" },
],
[
{ class_id: "Grade 2", roll_number: "7", name: "peter" },
{ class_id: "Grade 3", roll_number: "6", name: "lia" },
{ class_id: "Grade 2", roll_number: "5", name: "mary" },
{ class_id: "Grade 3", roll_number: "1", name: "violet" },
],
];
const result = data
.flat() // → [{...}, {...}, {...}, ...]
.reduce((acc, { class_id, roll_number, name }) => {
// 若该 class_id 尚未存在,则初始化
acc[class_id] ??= { class_id, students: [] };
// 向对应班级的 students 数组推入精简学生对象
acc[class_id].students.push({ roll_number, name });
return acc;
}, {});
console.log(Object.values(result));
// 输出:
// [
// { class_id: "Grade 1", students: [{ roll_number: "1", name: "alex" }, { roll_number: "2", name: "bob" }] },
// { class_id: "Grade 2", students: [{ roll_number: "7", name: "peter" }, { roll_number: "5", name: "mary" }] },
// { class_id: "Grade 3", students: [{ roll_number: "6", name: "lia" }, { roll_number: "1", name: "violet" }] }
// ]⚠️ 注意事项
- 兼容性:Array.prototype.flat() 和 ??=(Nullish Coalescing Assignment)需 Node.js ≥ 12(推荐 ≥ 14.17+)或现代浏览器支持。若需兼容旧环境,可用 [].concat(...data) 替代 flat(),用 acc[class_id] = acc[class_id] || { class_id, students: [] } 替代 ??=。
- 字段健壮性:代码假设每条记录必含 class_id、roll_number、name。生产环境中建议增加校验(如 if (!class_id) continue;)。
- 性能:该方案时间复杂度为 O(n),空间复杂度为 O(k),其中 n 是总学生数,k 是唯一班级数,高效且易于维护。
此模式不仅适用于学籍管理,也广泛用于日志聚合、订单分组、API 多请求结果合并等场景——关键在于用对象作临时索引容器,避免手动维护索引位置或重复查找。










