- 双重循环法(最传统,理解简单)
function unique(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
var flag = true;
for (var j = 0; j < result.length; j++) {
if (arr[i] === result[j]) {
flag = false;
break;
}
}
if (flag) {
result.push(arr[i]);
}
}
return result;
}
var arr = [1, 2, 3, 2, 1];
console.log(unique(arr)); // [1, 2, 3]
原理
- 外层遍历原数组每个元素,
- 内层遍历结果数组检查是否已存在,
- 没有就加入结果。
缺点:时间复杂度 O(n²),数组越大越慢。
- 利用 indexOf 去重(兼容 ES5)
function unique(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
if (result.indexOf(arr[i]) === -1) {
result.push(arr[i]);
}
}
return result;
}
var arr = [1, 2, 3, 2, 1];
console.log(unique(arr)); // [1, 2, 3]
同样时间复杂度 O(n²),但代码更简洁。
- 利用对象做哈希表(更快的实现)
function unique(arr) {
var result = [];
var seen = {};
for (var i = 0; i < arr.length; i++) {
if (!seen[arr[i]]) {
result.push(arr[i]);
seen[arr[i]] = true;
}
}
return result;
}
var arr = [1, 2, 3, 2, 1];
console.log(unique(arr)); // [1, 2, 3]
注意
- 这种方法对数字和字符串效果很好,时间复杂度接近 O(n)。
- 对于对象或数组类型的元素,会转成字符串 [object Object],可能会出错。
- 使用 forEach 结合 indexOf(兼容 ES5)
function unique(arr) {
var result = [];
arr.forEach(function(item) {
if (result.indexOf(item) === -1) {
result.push(item);
}
});
return result;
}
var arr = [1, 2, 3, 2, 1];
console.log(unique(arr)); // [1, 2, 3]
简单的 ES6 写法:
- 使用 Set(推荐)
let arr = [1, 2, 3, 2, 1];
let result = [...new Set(arr)];
console.log(result); // [1, 2, 3]
原理
- Set 是一种集合结构,自动去掉重复值。… 展开运算符可以把它再变回数组。
- 优点:简洁、可读性高
- 缺点:不支持对象深度去重(引用类型的比较是地址)
- 使用 filter + indexOf
let arr = [1, 2, 3, 2, 1];
let result = arr.filter((item, index) => arr.indexOf(item) === index);
console.log(result); // [1, 2, 3]
原理
- indexOf 会返回首次出现的索引,只有首次出现的位置和当前索引一致时才保留。
- 缺点:每次 filter 内部都要遍历一次(O(n²))
- 使用 reduce + includes
let arr = [1, 2, 3, 2, 1];
let result = arr.reduce((acc, cur) => {
if (!acc.includes(cur)) acc.push(cur);
return acc;
}, []);
console.log(result); // [1, 2, 3]
原理
- reduce 逐个累加,includes 检查是否已存在。
- 缺点:同样是 O(n²) 复杂度。
- 基于哈希表的去重(高效版)
let arr = [1, 2, 3, 2, 1];
let seen = {};
let result = arr.filter(item => {
if (!seen[item]) {
seen[item] = true;
return true;
}
return false;
});
console.log(result); // [1, 2, 3]
原理
- 利用对象键的唯一性,O(n) 时间就能完成去重。
- 优点:比 indexOf / includes 高效
- 缺点:对象键会自动转成字符串({} 和 “[object Object]” 都会冲突),处理对象数组时要加序列化。
- 去重并排序
let arr = [3, 1, 2, 3, 2, 1];
let result = [...new Set(arr)].sort((a, b) => a - b);
console.log(result); // [1, 2, 3]
- 对象数组去重(按某个字段)
假设有:
let arr = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
{ id: 1, name: "C" }
];
按 id 去重:
let seen = new Set();
let result = arr.filter(item => !seen.has(item.id) && seen.add(item.id));
console.log(result);
// [ { id: 1, name: "A" }, { id: 2, name: "B" } ]