一.前言
js中数组去重是老生常谈的问题,之所以是老生常谈,是因为这东西在平常的业务场景中挺常见的,是刚需;其次在面试中面试者也喜欢问,基本上是希望你能回答的越多越好,今天趁这个机会我正好捋一捋思路,把这块整理一下。
要说在前面的是,不同的业务场景决定不同的技术手段,也不存在一招通吃的函数或者算法。而且,大家一定不要被‘优雅’这两个字所桎梏,在业务中,能解决问题的办法,就是好办法;能解决问题的办法才有资格去优化性能和写法,变的更优雅。所以,初期面临问题的时候,我们先考虑的一定是怎么解决它,而不是一步想出一个既兼顾性能又兼顾写法的处理办法。
二.双重遍历
Array.prototype.unique = function () {
let temp = [];
let len = this.length;
let flag;
for (var i = 0; i < len; i++) {
flag = false;
for (var j = i + 1; j < len; j++) {
if (this[j] === this[i]) {
flag = true;
break;
}
}
if (!flag) {
temp.push(this[i])
}
}
return temp;
}
let result = [1,1,'1','1',0,0,'0','0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/].unique();
console.log('result', result);
// 输出 [ 1, '1', 0, '0', undefined, null, NaN, NaN, {}, {}, [], [], /a/, /a/ ]
双重遍历可以说是什么语言都通吃的去重方法了,也应该是很容易一开始就产生的思路。
从上面的输出我们可以看出,除了对NaN和对象,双重遍历完成了去重,在普通的业务场景下我们用它也可以完成需求。
三.单重循环
Array.prototype.unique = function () {
let arr = [];
let len = this.length;
for(var i = 0; i < len; i++){
if(arr.indexOf(this[i]) == -1){
arr.push(this[i])
}
}
return arr;
}
let result = [1,1,'1','1',0,0,'0','0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/].unique();
console.log('result', result);
// 输出 [ 1, '1', 0, '0', undefined, null, NaN, NaN, {}, {}, [], [], /a/, /a/ ]
单重循环其实就是建立在双重循环之上的优化方案,其实思路很简单,无非就是利用一个函数内的空数组去接收去过重的数组里的元素,判断依据就是通过元数组内的元素的下标去与这个空数组内未来会添加的元素下标做对比。
四.排序后去重
Array.prototype.unique = function () {
let arr = [];
//对原数组cancat()的意义在于其会返回一个新数组,排序后而不会修改原来的数组结构
let sortArr = this.concat().sort();
let len = sortArr.length;
let temp = sortArr[0];
for (var i = 0; i < len; i++) {
if ((!i || temp !== sortArr[i])) {
arr.push(sortArr[i]);
}
temp = sortArr[i];
}
return arr;
}
let result = [1, 1, '1', '1', 0, 0, '0', '0'].unique(); 普通重复数组去重是没问题的
let result2 = [1, 1, '1', '1', 0, 0, '0', '0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/].unique();
console.log(result)
// [ 0, '0', 1, '1' ]
console.log(result2)
//输出不正确,不仅仅NaN和对象类型没有去重,还有部分普通元素如'1'之类的也没有去重,根本问题在于sort()函数没有按照预期的目标去工作.
我们可以看出,排序后去重这一思路局限性比较多,元数据中在掺杂了如[],{},NaN之类的元素后并不能将相同的元素按相邻的原则排列在一起,主要还是受限于Array.sort()函数。所以我本人是不推荐你使用的。
五.对象Key去重
Array.prototype.unique = function () {
let rec = [];
let len = this.length;
let temp = {};
for(var i = 0;i < len;i++){
if(!temp[this[i]]){
temp[this[i]] = 1;
rec.push(this[i]);
}
}
return rec;
}
let result = [1, 1, '1', '1', 0, 0, '0', '0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/].unique()
console.log(result) //[ 1, 0, undefined, null, NaN, {}, [], /a/ ]
从输出我们可以看出,字符串的元素被去除了,但是实际上’1’和1明显不是一个东西,所以我们应该加一个类型判断的标识符:
Array.prototype.unique = function () {
let rec = [];
let len = this.length;
let temp = {};
for(var i = 0;i < len;i++){
var key = typeof this[i] + this[i];
if(!temp[key]){
temp[key] = 1;
rec.push(this[i]);
}
}
return rec;
}
let result = [1, 1, '1', '1', 0, 0, '0', '0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/].unique()
console.log(result) // [ 1, '1', 0, '0', undefined, null, NaN, {}, [], /a/ ]
此时,从输出的结果来看,这种优化后的利用key来去重的思路完美解决了需求。
六.ES6 Map 去重
Array.prototype.unique = function () {
let rec = [];
let len = this.length;
let temp = new Map();
for(var i = 0;i < len;i++){
if(!temp.get(this[i])){
temp.set(this[i], 1);
rec.push(this[i]);
}
}
return rec;
}
let result = [1, 1, '1', '1', 0, 0, '0', '0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/].unique()
console.log(result);
// [ 1, '1', 0, '0', undefined, null, NaN, {}, {}, [], [], /a/, /a/ ]
从输出我们可以看出,对象不去重,其他去重。
七.ES6 Set 去重
Array.prototype.unique = function () {
return [...new Set(this)]
}
let result = [1, 1, '1', '1', 0, 0, '0', '0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/].unique()
console.log(result)
// [ 1, '1', 0, '0', undefined, null, NaN, {}, {}, [], [], /a/, /a/ ]
从输出我们可以看出,对象不去重,其他去重,代码极其简单,就一行。