数组去重是面试中常见的一个算法问题。
数组去重,一般需求是给你一个数组,调用去重方法,返回数值副本,副本中没有重复元素。一般来说,两个元素通过 === 比较返回 true 的视为相同元素,需要去重,所以,1 和 “1” 是不同的元素,1 和 new Number(1) 是不同的元素,{}和 {} 是不同的元素(引用不同)。
方法一
利用另一个数组res,遍历原数组,如果res中没有的话就加入,如果已经存在的话就略过。最后返回res数组。
这是一种容易想到的O(n*n)的解法。
function unique(nums){
var res = [];
for(var i=0;i<nums.length;i++){
for(var j = 0;j<res.length;j++){
if(res[j] === nums[i]){
break;
}
}
if(j == res.length){
res.push(nums[i]);
}
}
return res;
}
代码非常简单,那么是否能更简洁些?如果不考虑浏览器兼容,我们可以用 ES5 提供的 Array.prototype.indexOf 方法来简化代码:
function unique(nums){
var res = [];
for(var i=0;i<nums.length;i++){
(res.indexOf(nums[i]) == -1) && res.push(nums[i]);
}
return res;
}
方法二
filter()是数组的一个迭代方法,每一个迭代方法都对数组中的每一项运行给定函数,此方法接受一个函数参数,此函数接收三个参数:
- 数组项的值 item
- 该项在数组中的位置 index
- 数组对象本身 array
filter()则返回该函数会返回true的项所组成的数组。
function unique(nums){
var res = nums.filter(function(item,index,array){
return array.indexOf(item) == index;
});
return res;
}
filter()中这个函数的含义是利用indexOf()检查每个项的位置是否等于index,由于indexOf()只会返回第一次出现的项的位置,因此如果有重复的数出现的话,这个函数返回false,此重复的数便不会被加入到返回的数组中。
方法三
将数组用 sort 排序后,理论上相同的元素会被放在相邻的位置,那么比较前后位置的元素就可以了。
function unique(nums){
nums.sort();
var res = [nums[0]];
for(var i=1;i<nums.length;i++){
if(nums[i] !== nums[i-1]){
res.push(nums[i]);
}
}
return res;
}
这种方法也可能出错,因为“1”和1会被排在一起,所以如果数组中存在1,’1’,1这样的情况,则会排错。
方法四
利用Object作为一个哈希表。
function unique(nums){
var hash = {};
var res = nums.filter(function(item){
return hash[item]?false:(hash[item] = true);
});
return res;
}
因为 Object 的 key 值都是 String 类型,所以对于 1 和 “1” 无法分别,我们可以稍微改进下,将类型也存入 key 中:
function unique(nums){
var hash = {};
var res = [];
for(var i=0;i<nums.length;i++){
var key = typeof nums[i] + nums[i];
if(!hash[key]){
hash[key] = true;
res.push(nums[i]);
}
}
return res;
}
方法五
利用ES6的Set结构,由于Set存储的都是不重复的值,因此可以将数组传给Set生成一个没有重复数的set对象,再转换回数组。
可以使用Array.from:
Array.from(new Set(arr));
也可以使用扩展操作符
[...new Set(arr)]