一.前言
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/ ]
從輸出我們可以看出,對象不去重,其他去重,代碼極其簡單,就一行。