1. 程式人生 > >六種方法實現JavaScript陣列去重

六種方法實現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將集合轉為陣列返回