JavaScript开发过程中,经常会遇到多个数组对象合并的需求。例如接口返回两批用户数据、商品列表分页加载、权限集合合并等场景,都需要将两个数组合并成一个完整的数据集合。同时,由于不同数据源可能存在重复记录,通常还需要根据对象中的唯一标识字段(如ID)进行去重。
本文将详细介绍JavaScript中合并两个数组对象并根据ID去重的多种实现方式,包括ES6常用方法、Map数据结构处理方案以及传统循环方式,帮助开发者根据实际业务需求选择合适的方案。
一、需求分析:合并数组对象并按照ID去重
假设有两个用户数组:
JavaScriptconst arr1 = [ { id: 1, name: "张三", age: 20 }, { id: 2, name: "李四", age: 25 }, { id: 3, name: "王五", age: 30 } ]; const arr2 = [ { id: 2, name: "李四", age: 25 }, { id: 3, name: "王五", age: 31 }, { id: 4, name: "赵六", age: 28 } ];
两个数组合并后,希望得到:
JavaScript[ { id: 1, name: "张三", age: 20 }, { id: 2, name: "李四", age: 25 }, { id: 3, name: "王五", age: 30 }, { id: 4, name: "赵六", age: 28 } ]
其中:
-
id为1的数据保留;
-
id为2、3的数据只保留一份;
-
id为4的数据新增;
-
相同ID的数据根据规则决定保留哪一个。
二、使用Map实现数组对象根据ID去重(推荐)
ES6中的Map结构非常适合处理对象数组去重问题。
Map具有键唯一的特点,如果设置相同key,后面的值会覆盖前面的值,因此可以直接利用对象ID作为Map的key。
示例:
JavaScriptconst result = Array.from( new Map( [...arr1, ...arr2].map(item => [item.id, item]) ).values() ); console.log(result);
执行过程:
-
使用扩展运算符合并两个数组:
JavaScript[...arr1, ...arr2]
得到:
JavaScript[ {id:1}, {id:2}, {id:3}, {id:2}, {id:3}, {id:4} ]
-
使用map转换为Map键值结构:
JavaScript[item.id, item]
例如:
JavaScript[ [1, {id:1}], [2, {id:2}], [3, {id:3}] ]
-
Map自动根据ID去重。
-
使用values()获取最终对象集合。
这种方式代码简洁,执行效率较高,是现代JavaScript项目中比较推荐的写法。
三、根据ID去重并保留后面的数据
Map默认会覆盖前面的数据,因此:
JavaScriptconst arr = [ {id:1,name:"A"}, {id:1,name:"B"} ]; const result = Array.from( new Map(arr.map(item => [item.id,item])).values() ); console.log(result);
输出:
JavaScript[ { id:1, name:"B" } ]
原因是后面的对象覆盖了前面的对象。
这种方式适合:
-
最新接口数据覆盖旧数据;
-
后加载的数据优先;
-
更新列表数据。
例如:
JavaScriptconst merged = Array.from( new Map( [...oldList,...newList] .map(item => [item.id,item]) ).values() );
新列表中的相同ID数据会覆盖旧数据。
四、保留前面的数据,根据ID去重
有些业务场景希望第一次出现的数据优先,例如:
-
本地缓存优先;
-
历史数据优先;
-
固定排序需求。
可以使用Set配合filter:
JavaScriptconst merged = [...arr1, ...arr2]; const ids = new Set(); const result = merged.filter(item => { if(ids.has(item.id)){ return false; } ids.add(item.id); return true; }); console.log(result);
运行逻辑:
第一次遇到ID:
JavaScriptids.has(item.id)
返回false,保留数据。
再次遇到相同ID:
返回true,过滤掉。
最终保留第一次出现的数据。
五、使用reduce实现数组对象去重
reduce也是处理数组合并去重的常用方法。
示例:
JavaScriptconst result = [...arr1, ...arr2].reduce((prev, item) => { const exist = prev.find( value => value.id === item.id ); if(!exist){ prev.push(item); } return prev; }, []); console.log(result);
实现思路:
-
创建空数组;
-
遍历合并后的数据;
-
判断当前ID是否已经存在;
-
不存在则加入结果数组。
这种方式代码容易理解,适合初学者。
不过需要注意:
JavaScriptfind()
每次都会遍历已有数组。
如果数据量较大,例如几万条数据,性能会明显低于Map方案。
六、处理多个数组对象合并去重
实际项目中可能不是两个数组,而是多个数组:
JavaScriptconst list1 = []; const list2 = []; const list3 = [];
可以继续使用展开运算符:
JavaScriptconst result = Array.from( new Map( [ ...list1, ...list2, ...list3 ].map(item => [ item.id, item ]) ).values() );
这种方式可以无限扩展,适合处理多个接口数据合并。
七、根据指定字段进行动态去重
有时候唯一标识不是id,而可能是:
-
userId
-
productId
-
code
-
uuid
可以封装成通用函数:
JavaScriptfunction mergeUnique(arr1, arr2, key){ return Array.from( new Map( [...arr1,...arr2] .map(item => [ item[key], item ]) ).values() ); }
调用:
JavaScriptconst result = mergeUnique( users1, users2, "id" );
如果按照商品编号:
JavaScriptconst result = mergeUnique( products1, products2, "productId" );
这样可以提高代码复用性。
八、处理ID类型不一致的问题
实际开发中,经常出现:
JavaScript{ id:1 }
和:
JavaScript{ id:"1" }
这种情况。
Map会认为:
JavaScript1 !== "1"
因此无法去重。
解决方式:
统一ID类型:
JavaScriptconst result = Array.from( new Map( [...arr1,...arr2] .map(item => [ String(item.id), item ]) ).values() );
这样:
JavaScript1
和:
JavaScript"1"
都会转换成:
JavaScript"1"
避免重复数据。
九、数组对象合并去重时的数据覆盖策略
根据业务需求不同,重复ID的数据处理方式也不同。
1. 后者覆盖前者
适合:
-
最新数据更新;
-
接口同步。
实现:
JavaScriptnew Map( [...oldData,...newData] .map(item=>[item.id,item]) )
2. 前者覆盖后者
适合:
-
本地缓存优先;
-
固定配置优先。
实现:
JavaScriptnew Map( [...newData,...oldData] .map(item=>[item.id,item]) )
调整数组顺序即可。
3. 合并两个对象属性
如果两个对象ID相同,但字段不同,可以使用对象合并:
JavaScriptconst result = Array.from( new Map( [...arr1,...arr2] .map(item=>[ item.id, { ...item } ]) ).values() );
也可以:
JavaScriptconst map = new Map(); [...arr1,...arr2].forEach(item=>{ if(map.has(item.id)){ map.set( item.id, { ...map.get(item.id), ...item } ); }else{ map.set(item.id,item); } }); const result = [...map.values()];
这种方式适合处理复杂对象合并。
十、性能对比分析
不同去重方式性能差异:
| 方法 | 时间复杂度 | 适用场景 |
|---|---|---|
| Map | O(n) | 大数据量推荐 |
| Set+filter | O(n) | 简单去重 |
| reduce+find | O(n²) | 数据量较小 |
| 双重循环 | O(n²) | 传统写法 |
如果数组只有几十条数据,几种方式都可以。
如果数据达到:
-
1万条;
-
10万条;
-
大量接口合并;
建议优先选择Map方案。
十一、实际项目中的封装示例
在Vue、React等前端项目中,可以封装公共方法:
JavaScriptexport function uniqueById(list){ return Array.from( new Map( list.map(item=>[ item.id, item ]) ).values() ); }
使用:
JavaScriptconst data = uniqueById([ ...pageData1, ...pageData2 ]);
可以方便地处理:
-
分页数据合并;
-
无限滚动加载;
-
搜索结果合并;
-
多接口数据整合。
总结
JavaScript合并两个数组对象并根据ID去重,是前端开发中非常常见的数据处理需求。
对于现代浏览器环境,推荐使用Map结合数组展开运算符:
JavaScriptconst result = Array.from( new Map( [...arr1,...arr2] .map(item=>[item.id,item]) ).values() );
该方案代码简洁、性能优秀,并且可以灵活控制重复数据的覆盖规则。
如果需要保留首次出现的数据,可以结合Set和filter实现;如果需要处理复杂对象合并,则可以在Map基础上增加对象展开逻辑。根据数据规模和业务规则选择合适的方法,可以让数组处理代码更加稳定、高效。