假设原数组:
ini
const arr = [1, 2, 2, 3, 4, 4, 4, 5, 1];
1. 双 for 循环(最基础、兼容性最好)
思路:外层遍历,内层对比,发现重复就删除。
ini
function unique1(arr) {
// 浅拷贝原数组,避免修改原数组
const newArr = [...arr];
for (let i = 0; i < newArr.length; i++) {
for (let j = i + 1; j < newArr.length; j++) {
// 如果发现重复,删除当前项
if (newArr[i] === newArr[j]) {
newArr.splice(j, 1);
// 删除后下标回退,防止跳过元素
j--;
}
}
}
return newArr;
}
console.log(unique1(arr)); // [1,2,3,4,5]
使用双重for循环解决数组去重问题,他的时间复杂度为(O(n^2)),空间复杂度为(O(1))
2. indexOf 方法
思路:判断元素第一次出现的位置是否等于当前下标,不是则重复。
ini
function unique2(arr) {
const newArr = [];
for (let i = 0; i < arr.length; i++) {
// 第一次出现才 push
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
console.log(unique2(arr)); // [1,2,3,4,5]
indexOf() 是js里提供的查找数组元素下标的api,是数组 / 字符串 都能用的方法,作用是:查找某个元素第一次出现的位置,返回它的下标;找不到就返回 -1。 每次调用 indexOf 都会从头遍历数组,所以时间复杂度也为(O(n^2))。
3. filter 过滤器(简洁优雅)
思路 :利用 filter 保留第一次出现的元素。
javascript
function unique3(arr) {
return arr.filter((item, index) => {
// 只保留第一次出现的元素
return arr.indexOf(item) === index;
});
}
console.log(unique3(arr)); // [1,2,3,4,5]
Array.prototype.filter(),js提供的一种过滤器方法。它会遍历数组里的每一项 ,你写一个条件,满足条件的留下,不满足的扔掉 ,最后返回一个新数组。
javascript
数组.filter(function(当前项, 下标, 原数组){
return 满足条件就返回 true / false
})
最常用的就两个:
- item:当前正在遍历的元素
- index:当前元素的下标
因此filter+indexOf()的时间复杂度为(O(n^2))
filter 3 大核心特点
- 返回新数组,不影响原来的数组
- 必须写 return,不写会全过滤掉
- 遍历数组里每一项,自动循环
4. sort 排序 + 相邻对比
思路:先排序,重复元素会挨在一起,再遍历去重。
ini
function unique4(arr) {
if (!arr.length) return [];
// 先排序
const sortedArr = [...arr].sort();
const newArr = [sortedArr[0]];
for (let i = 1; i < sortedArr.length; i++) {
// 和前一个不同就保留
if (sortedArr[i] !== sortedArr[i - 1]) {
newArr.push(sortedArr[i]);
}
}
return newArr;
}
console.log(unique4(arr)); // [1,2,3,4,5]
.sort() 用于数组排序 ,直接修改原数组,返回排序后的原数组引用
JS 引擎排序算法:主流为 Timsort,时间复杂度 (O(n\log n))
5. Map / Object 哈希表(性能最优之一)
思路:用 Map 记录元素是否出现过,O (n) 复杂度。
arduino
function unique5(arr) {
const map = new Map();
const newArr = [];
for (let item of arr) {
// 不存在则存入
if (!map.has(item)) {
map.set(item, true);
newArr.push(item);
}
}
return newArr;
}
console.log(unique5(arr)); // [1,2,3,4,5]
Map 是键值对集合 ,键可以是任意类型(数字、字符串、对象等),键唯一,有序(按插入顺序
常用 MAP的API
arduino
const map = new Map();
map.set(key, value) // 添加/修改键值对
map.has(key) // 判断键是否存在,返回布尔
map.get(key) // 获取对应值
map.delete(key) // 删除指定键
map.clear() // 清空
map.size // 获取元素个数
结合去重的原理
- 遍历原数组,把元素当作
Map的key - 每次先用
map.has(item)判断是否已存在 - 不存在则存入 Map + 推入结果数组,利用Key 唯一性去重
特点
- 查询、插入效率极高,近似 (O(1)),整体遍历 (O(n))
- 可以正确识别
NaN、不同引用的对象(优于普通 Object) - 有序,保留元素原始插入顺序
- 额外开辟哈希空间,空间复杂度 (O(n))
6. Set 数据结构(ES6 最简洁一行)
思路:Set 天生不允许重复,再转回数组。
javascript
function unique6(arr) {
// 一行搞定
return [...new Set(arr)];
}
// 或简写
// const unique6 = arr => [...new Set(arr)];
console.log(unique6(arr)); // [1,2,3,4,5]
Set 是元素集合 ,特点:元素唯一、不重复、有序(插入顺序) ,是JS专为去重场景设计的数据结构。
常用 API
arduino
const set = new Set();
set.add(value) // 添加元素
set.has(value) // 判断是否存在
set.delete(value) // 删除元素
set.clear() // 清空
set.size // 元素个数
数组 ↔ Set 互转
js
javascript
// 数组转 Set(自动去重)
new Set(arr)
// Set 转回数组(两种写法)
[...new Set(arr)] // 扩展运算符
Array.from(new Set(arr))
去重原理
Set 底层基于哈希表,不允许存储重复值,数组传入瞬间自动剔除重复项,再转回数组即可。
特点
- 代码最简洁,一行完成去重
- 时间复杂度 (O(n)),性能优秀
- 支持
NaN去重(传统 indexOf 做不到) - 保留原始顺序,ES6+ 环境首选
- 无法直接获取下标,只适合单纯去重场景