六種方法實現JavaScript陣列去重
JavaScript陣列去重問題雖然在實際開發中很少應用
但卻是面試(或筆試)中可能被問到的題
如果只能說出一兩種方法的話,就顯得我們很low
所以這裡我總結了陣列去重的六種方法供大家參考
直接遍歷
首先先來一種簡單粗暴的方法
也是很容易就可以想到的辦法
宣告一個新陣列
直接遍歷這個待去重陣列
然後把新陣列中沒有的元素推進去
function unique(arr){
var newArr = [];
for(var i = 0, item; item = arr[i++];){
if(newArr.indexOf(item) === -1){
newArr.push(item);
}
}
return newArr;
};
測試陣列
var arr = [1,3,2,1,4,5,2,4,1,5];
console.log(unique(arr)); //[1,3,2,4,5]
這個結果是正確的
但是如果元素中有undefined或者null就會出現問題
var arr = [1,3,2,1,null,4,5,2,4,1,5,null];
console.log(unique(arr)); //[1,3,2]
這是因為for(var i = 0, item; item = arr[i++];)
null導致提前跳出了循壞
沒辦法雖然我很喜歡這種迴圈方式
但是為了保持嚴謹還是要使用正常的for迴圈
function unique(arr){
var newArr = [];
var item;
for(var i = 0, len = arr.length; i < len; i++){
item = arr[i];
if(newArr.indexOf(item) === -1){
newArr.push(item);
}
}
return newArr;
};
var arr = [1,3,2,1,null,4,5,2,4,1,5,null];
console.log(unique(arr)); //[1,3,2 ,null,4,5]
當然也可以使用ES5的forEach方法
雖然它沒有for迴圈的效率高
function unique(arr){
var newArr = [];
arr.forEach(function(item){
if(newArr.indexOf(item) === -1){
newArr.push(item);
}
});
return newArr;
}
下面的其他方法也可以把for迴圈替換成forEach
除此之外,我們還可以使用ES5的reduce方法
讓程式碼看起來更加高大上
function unique(arr){
return arr.reduce(function(prev, next){
if(prev.indexOf(next) === -1){
prev.push(next);
}
return prev;
}, []);
}
indexOf也可以替換成迴圈判斷
不過既然有好使的API就直接拿來用了
順便一提,方法不能夠處理陣列NaN的去重
var arr = [4, 2, 1, 3, 2, 3, NaN, NaN];
console.log(unique(arr)); //[4,2,1,3,NaN,NaN]
包括下面的方法都不可以
不過可以把indexOf()換成ES6的Array.contains()
真正意義上的絕對相等
這裡我就不討論太複雜的情況了
索引判斷
這種方法是利用了陣列indexOf的特點
它會找到陣列中第一個該元素值的索引
所以我們可以判斷陣列元素的indexOf索引判斷和元素本身的索引是否相同
如果相同,代表這是陣列第一次出現的該元素值
function unique(arr){
var newArr = [];
var item;
for(var i = 0, len = arr.length; i < len; i++){
item = arr[i];
if(arr.indexOf(item) === i){
newArr.push(item);
}
}
return newArr;
}
我們還可以做一些小小的優化
因為實際上第一次是不需要判斷的
陣列元素的第一個值一定是首次出現的
所以從陣列的第二個元素開始迴圈就可以了
function unique(arr){
var newArr = [arr[0]];
var item;
for(var i = 1, len = arr.length; i < len; i++){
item = arr[i];
if(arr.indexOf(item) === i){
newArr.push(item);
}
}
return newArr;
}
排序去鄰
這種方法的原理就是首先呼叫陣列的sort方法
我們的目的不是給陣列元素排序
所以也不需要為sort新增處理函式
目的是為了把相同的元素值聚在一起
這樣只需要判斷陣列元素值和上一個索引值不同就可以了
function unique(arr){
var newArr = [arr[0]];
var item;
arr.sort();
for(var i = 1, len = arr.length; i < len; i++){
item = arr[i];
if(item !== arr[i - 1]){
newArr.push(item);
}
}
return newArr;
}
優化遍歷
這種方法看起來特別夢幻
原理是不斷的將陣列最右邊不重複的值推入新陣列
function unique(arr){
var newArr = [];
for(var i = 0, len = arr.length; i < len; i++){
for(var j = i + 1; j < len; j++){
if(arr[i] === arr[j]){
j = ++i;
}
}
newArr.push(arr[i]);
}
return newArr;
};
說的再詳細一下
就是對陣列的每一個元素都進行判斷(指標i)
還有另一個指標從判斷元素的下一位進行判斷
移動這個指標(指標j下移)
如果發現判斷元素與指標指向的值相等
證明該判斷元素不是陣列中唯一的
那麼就繼續往下判斷(指標i下移,指標j回到i的下一位)
直到j移到陣列終點
證明判斷元素(指標i指向的元素)是陣列中唯一的
推入新陣列
臨時物件
這種方法是很好的一種方法
借用了一個臨時物件的資料結構
這個物件用來儲存陣列的元素
function unique(arr){
var newArr = [];
var temp = {};
var item;
for(var i = 0, len = arr.length; i < len; i++){
item = arr[i];
if(!temp[item]){
temp[item] = true;
newArr.push(item);
}
}
return newArr;
}
這是典型的空間換取時間思想的演算法
如果陣列很大可能會很佔記憶體
但是效率很高這是毋庸置疑的
其實現在這個函式還是有缺點
var arr = [1,3,2,1,4,5,2,4,1,5,'1','2'];
console.log(unique(arr)); //[1,3,2,4,5]
從這個測試中可以看到陣列和字元沒能有效區分
這是因為它們傳入物件中會呼叫toString()
物件的鍵都是字串
既然這樣我們可以對函式做一些修改
讓物件的鍵對應一個數組
陣列儲存著已有的型別
function unique(arr){
var newArr = [];
var temp = {};
var item;
var type;
for(var i = 0, len = arr.length; i < len; i++){
item = arr[i];
type = typeof item;
if(!temp[item]){
temp[item] = [type];
newArr.push(item);
}else if(temp[item].indexOf(type) === -1){
temp[item].push(type);
newArr.push(item);
}
}
return newArr;
}
這樣記憶體佔用更大了
但是卻更加嚴謹了
var arr = [1,3,2,1,4,5,2,4,1,5,'1','2'];
console.log(unique(arr)); //[1,3,2,4,5,"1","2"]
集合轉換
集合這個資料結構最大的特點就是
集合內部的元素都是唯一的
ES6標準給我們提供了Set集合
利用這個新的資料結構我們可以很容易的實現陣列去重
容易到什麼程度呢?
一行..
就夠了
function unique(arr){
return Array.from(new Set(arr));
}
這才是真正的簡單粗暴
將陣列轉為集合,拋棄多餘數值
最後利用ES6的新陣列方法Array.from將集合轉為陣列返回