前言
在JavaScript的世界里,数组如同一片广袤的沙漠,重复元素则是散落其中的碎石。当我们需要从这片沙海中淘出纯净的"金子"时,数组去重便成为不可或缺的工具——它能避免重复数据导致的资源浪费和用户体验问题,确保数据处理的高效与精准。
本文将专注于介绍最实用的数组去重方法,助你在代码的沙海中快速找到最优的解决方案👏
一、淘金术大比拼:最实用的数组去重方法
1. Set淘金法:最高效的"沙金筛"
Set是ES6为我们带来的高效淘金工具,它就像一个精密的筛子,能自动过滤掉所有重复的"沙粒"。
const arr = [1, '1', 2, 2, NaN, NaN];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, '1', 2, NaN]
Set内部基于哈希表实现,查找和插入操作接近O(1)时间复杂度,整体效率为O(n)。能正确区分数字1和字符串'1',并自动处理NaN。这就像用筛子淘金,只需一遍过筛就能得到纯净的金粒。
在JavaScript中,
NaN是一个特殊的值,它不等于任何值,包括它自己(NaN !== NaN)。
2. Map淘金法:精准的"金粒分类器"
Map方法通过利用Map数据结构的键唯一性特性,为每个元素创建一个唯一标识:
function uniqueByMap(arr) {
const map = new Map();
return arr.filter(item => {
if (!map.has(item)) {
map.set(item);
return true;
}
return false;
});
}
Map的键可以是任意值类型,包括对象和原始类型,这使得它能正确处理不同类型值的区分,如1和'1'。不过 Map 对对象 key 按引用判断,内容相同的不同对象不会被去重。
3. Filter+FindIndex组合法:灵活的"金粒筛选器"
Filter与findIndex的组合方法允许我们自定义去重条件,特别适合对象数组:
// 对象数组按id去重
const arr = [{id:1, name:'Alice'}, {id:2, name:'Bob'}, {id:1, name:'Charlie'}];
const uniqueArr = arr.filter((item, index, self) => {
return self.findIndex(t => t.id === item.id) === index;
});
console.log(uniqueArr); // [{id:1, name:'Alice'}, {id:2, name:'Bob'}]
findIndex方法允许我们定义自定义比较逻辑,可以按对象的特定属性(如id、name)去重。但是时间复杂度为O(n²),不适合大数据集。
4. 排序后相邻比较法:高效的"金粒排列术"
排序后相邻比较法通过先对数组排序,然后只需一次遍历就能发现相邻重复元素:
function uniqueBySort(arr) {
if (arr.length === 0) return [];
const sortedArr = [...arr].sort(); // 创建副本,避免修改原数组
const result = [sortedArr[0]];
for (let i = 1; i < sortedArr.length; i++) {
if (sortedArr[i] !== sortedArr[i - 1]) {
result.push(sortedArr[i]);
}
}
return result;
}
const arr = [3, 1, 2, 2, 4, 5];
console.log(uniqueBySort(arr)); // [1, 2, 3, 4, 5] (原顺序被改变)
排序后只需一次遍历,时间复杂度O(n log n)。但会改变原数组顺序(已通过[...arr]避免修改原数组)。
5. JSON.stringify转换法:对象数组的"指纹识别"
JSON.stringify方法通过将对象转换为字符串,利用字符串的唯一性来实现去重:
function uniqueByJSON(arr) {
const seen = new Set(); // 使用Set替代对象,提高效率
return arr.filter(item => {
const str = JSON.stringify(item);
if (!seen.has(str)) {
seen.add(str);
return true;
}
return false;
});
}
const arr = [{id:1, func: function(){}}, {id:1}];
console.log(uniqueByJSON(arr)); // [{id:1}] (将两个对象视为相同)
JSON字符串可以唯一表示对象的结构和内容,能处理对象的深度比较(如[{id:1}, {id:1}]会被视为相同)。但无法处理函数、循环引用等特殊值(如示例中函数被忽略导致错误去重)。
二、实战建议:选择最适合的淘金工具
在实际项目中,我们应根据数据类型和规模选择合适的淘金方法:
-
处理简单数据:优先使用Set方法,简洁高效。
-
处理对象数据:
- 若只需按简单属性去重,使用
filter+findIndex组合 - 若需要深度比较,使用
JSON.stringify方法
- 若只需按简单属性去重,使用
-
处理大数据量:
- 对于基本类型数组,使用Set或排序后相邻比较法
- 对于对象数组,使用Map或
filter+findIndex组合
-
处理特殊值:
- 使用Set方法可自动处理NaN
- 对于对象中的NaN属性,可在键生成逻辑中特殊处理
小技巧:在实际应用中,可以结合使用Set和Map来处理更复杂的情况:
// 对象数组按id去重
let uniqueArr = [...new Map(arr.map(item => [item.id, item])).values()];
// 处理包含NaN的数组
let uniqueArrWithNaN = [...new Set(arr)];
三、特殊场景的淘金技巧:多维数组去重
多维数组的去重可以使用JSON.stringify,但也有其局限性:
function uniqueMultiDimensional(arr) {
const seen = new Set();
return arr.filter(item => {
const str = JSON.stringify(item);
if (!seen.has(str)) {
seen.add(str);
return true;
}
return false;
});
}
那么其“多维”与“局限性”都是怎么体现的呢?让我用两个数组作为例子来讲清楚:
// 示例1:普通多维数组
const multiArr = [[1, 2], [3, 4], [1, 2], [5, 6]];
console.log(uniqueMultiDimensional(multiArr));
// [[1, 2], [3, 4], [5, 6]] ✅ 正确去重
// 示例2:包含函数的多维数组(局限性体现)
const arrWithFunc = [[1, 2, function(){}], [1, 2]];
console.log(uniqueMultiDimensional(arrWithFunc));
// [[1, 2, function(){}]] ❌ 错误去重(函数被忽略,导致两个不同数组被视为相同)
结语:淘金不止于去重
数组去重不仅是前端开发中的基础技能,更是对数据处理思维的锻炼。它教会我们如何在纷繁复杂的数据中找到价值,如何选择最合适的工具来解决问题。
记住:没有一种方法能解决所有去重问题。Set方法简洁高效,但无法处理对象深度比较;Map方法安全可靠,但需要配合键生成逻辑;filter+findIndex灵活多样,但性能一般;排序后相邻比较法高效简洁,但会改变顺序。
在未来的开发中,当你面对重复数据时,不妨先问自己:这是一片细沙还是金矿?需要一把简单的筛子,还是一位经验丰富的金匠?选择正确的"淘金术",让数据处理变得高效而优雅。
如发现错误或有更好方法,欢迎在评论区留言交流!