JS数组去重的九种高阶方法(亲测有效)

作者:纸飞机时间:2026-10-24 09:22:04浏览:351

本文深入解析JavaScript中数组去重的九种高阶实现方式。从利用对象Key唯一性、交换元素替代splice,到filter结合indexOf、hasOwnProperty,再到reduce配合includes,以及indexOf、includes、Set扩展运算符和Map结构的多种组合。每种方法均附带原理说明与代码示例,帮助开发者根据实际场景选择最优解。

前言

常见的去重手段此处不再赘述,例如双层循环比对或新建数组后push等。需特别注意,使用splice移除元素时可能引发数组塌陷,需额外处理索引问题。

本文汇总了多种数组去重方案,重点展示高阶方法及API的使用,并附带语法解释与逻辑分析。其他组合调用方式原理相似,例如for循环可转换为forEach,故不再逐一列举。若读者有更优算法,欢迎留言交流。

设定测试数组为 [1,2,2,4,null,null,'3','abc',3,5,4,1,2,2,4,null,null,'3','abc',3,5,4],目标是去除其中的重复项。


let arr = [1,2,2,4,null,null,'3','abc',3,5,4,1,2,2,4,null,null,'3','abc',3,5,4] 

1. 利用对象的 key 唯一

对象键名具有唯一性,重复赋值会覆盖前者。基于此特性,遍历数组并将每项作为对象键名,即可实现去重。


let obj = {};
for (let i = 0; i < arr.length; i++) {
 let item = arr[i]
 if (obj[item] !== undefined) {
 arr.splice(i, 1);
 i--; // 解决删除元素后,数组塌陷问题
 continue;
 }
 obj[item] = item
}

// arr: [1, 2, 4, null, "3", "abc", 3, 5]

2. 交换元素位置从而替换调 splice方法

前述基于splice的方法存在性能瓶颈。删除元素后,后续所有元素索引需前移,数据量大时显著影响效率。

优化方案是将重复元素与数组末尾元素交换位置,随后i--重新判断,同时length--缩短数组长度。这种方式避免移动其他元素索引,提升执行效率。


let obj = {};
for (let i = 0; i < arr.length; i++) {
 let item = arr[i]
 if (obj[item] !== undefined) {
 arr[i] = arr[arr.length-1]
 arr.length--;
 i--; 
 continue;
 }
 obj[item] = item
}
// arr: [1, 2, 4, null, "3", "abc", 3, 5]

3. Array.filter + Array.indexOf

filter() 方法用于创建新数组,包含原数组中满足指定条件的所有元素。若无匹配项,则返回空数组。

语法:array.filter(function(item,index,arr))

  • filter() 不会对空数组进行检测。
  • filter() 不会改变原始数组。

原理:筛选出item首次出现位置索引等于当前index的元素。


let newArr = arr.filter((item, index) => arr.indexOf(item) === index); 

// [1, 2, 4, null, "3", "abc", 3, 5]

4. Array.filter + Object.hasOwnProperty

hasOwnProperty() 方法返回布尔值,指示对象自身属性中是否包含指定属性。

原理:利用对象键名不可重复的特性,通过类型与值组合构建唯一键进行判断。


let obj = {}

arr.filter(item => obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)) 

5. Array.reduce + Array.includes

reduce() 方法接收一个累加函数,从左至右遍历数组,最终合并为一个值。

语法:arr.reduce(function(total, currValue, currIndex, arr), initValue)

reduce() 对于空数组是不会执行回调函数的。

total:必需。初始值, 或者计算结束后的返回值

currValue:必需。当前元素

currIndex:可选。当前元素的索引

arr :可选。当前数组对象。

initValue:可选。累加器初始值

一个空数组调用reduce()方法且沒有提供初始值,会报错。

一个空数组调用reduce()方法且提供了初始值,将直接返回该初始值,不會调用 callback 函数。

非空数组调用reduce()提供初始值,则total将会等于初始值,且 currValue从第一个元素开始;若沒有提供初始值,则 total 会等于的第一个元素值,且 currValue将会从第二个元素开始。


let newArr = arr.reduce((accu, cur) => {
 return accu.includes(cur) ? accu : accu.concat(cur); // 1. 拼接方法
 // return accu.includes(cur) ? accu : [...accu, cur]; // 2. 扩展运算
}, []);


// [1, 2, 4, null, "3", "abc", 3, 5]

6. Array.indexOf

indexOf() 方法返回数组中指定元素第一次出现的索引。遍历查找,未找到则返回 -1。


let newArr = []
for (var i = 0; i < arr.length; i++) {
 if (newArr.indexOf(arr[i]) === -1) newArr.push(arr[i]) 
}

//等同于 forEach 写法
arr.forEach( item => newArr.indexOf(item) === -1 ? newArr.push(item) : '')

console.log(newArr) // [1, 2, 4, null, "3", "abc", 3, 5]

7. Array.includes

includes() 方法判断数组是否包含指定值,包含返回 true,否则返回 false。


let newArr = []
for (var i = 0; i < arr.length; i++) {
 if (!newArr.includes(arr[i])) newArr.push(arr[i])
}

//等同于 forEach 写法
arr.forEach( item => !newArr.includes(item) ? newArr.push(item) : '')

console.log(newArr) // [1, 2, 4, null, "3", "abc", 3, 5]

8. new Set + 扩展运算符 || Array.from

ES6 提供了新的数据结构 Set。类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,可以接受一个具有 iterable 接口数据结构作为参数(如数组,字符串),用来初始化。


let newArr = [...new Set(arr)];  // [1, 2, 4, null, "3", "abc", 3, 5]

let newArr = Array.from(new Set(arr));  // [1, 2, 4, null, "3", "abc", 3, 5]

let newStr = [...new Set('ababbc')].join('') // 'abc'

9. new Map

ES6 提供了新的数据结构 Map。类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。

get方法读取key对应的键值,如果找不到key,返回undefined。

has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。


let map = new Map();
let newStr = [];

for (let i = 0; i < arr.length; i++) {
 if (!map.has(arr[i])) {
  map.set(arr[i], true);
  newStr.push(arr[i]);
 }
}
console.log(newArr) // [1, 2, 4, null, "3", "abc", 3, 5]

总结

到此这篇关于JS数组去重的九种高阶方法的文章就介绍到这了,更多相关JS数组去重内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

作者最新文章