Js數組元素去重問題(吐血整理)十一種方法

前段時間遇到數組去重的問題,網上查到的資料也是參差不齊,有的實用有的簡潔有的好記,所以在此把遇到的一些方法做一個記錄,同時對一些常用方法時間消耗、以及難易程度做個比較。

Ps所有代碼均在Firefox內做過測試。

 

一、利用es6中的set去重

set 是es6中新的數據結構,類似於數組,但其成員唯一,沒有重複的值,無序。一般用[...myset]的形式轉化爲數組之後遍歷。優點是代碼簡潔方便記憶速度也較快,缺點是……額還沒發現缺點,要不然爲啥放第一個23333

function func(arr){
    return Array.from(new Set(arr));
}
var arr=[1,2,3,4,4,4,NaN,NaN,'NaN',true,'true',null,null,{},{},{}];
console.log(func(arr));

這是在Firefox瀏覽器內測試的結果,注意看到還無法去掉空對象

二、使用map

Map是js中的鍵值對結構,其查找速度極快。用其排序原理即遍歷數組,向map內添加鍵值對,由於鍵不允許存在重複,自然利用map存儲的元素也不會重複了。優點是效率極高,佔用空間也較小,缺點是需要熟練掌握map用法。

var funcmap = function(arr){
    let temp = new Map();
    return arr.filter(item=>{return !temp.has(item) && temp.set(item,1);});
}

同上set,一樣無法去掉空對象

三、使用對象屬性無重複來對數組去重

此法相比上個方法由於是向對象內添加屬性,其操作速度自然不如以上,但可以去除空對象,以及類似於字符串NaN,字符串true這種。(後文解釋爲什麼會去除字符串NaN,字符串true)

var funcobject = function(arr) {
  var array = [];
  var obj = {};
  for (var i = 0; i < arr.length; i++) {
    if (!obj[arr[i]]) {
      array.push(arr[i]);
      obj[arr[i]] = 1;
    } else {
      obj[arr[i]]++;
    }
  }
  return array;
};

四、利用sort去重

Sort去重原則上還是判斷是否相等。相比上面鍵值對,對象屬性等騷操作速度自然是不如了,不過sort支持度廣泛,容易理解,排序速度也不是太慢。

var funcsort = function(arr) {
  arr.sort();
  var array = [];
  for (var i = 1; i < arr.length; i++) {
    if (arr[i] !== arr[i - 1]) {
      array.push(arr[i - 1]);
    }
  }
  return array;
};

對於空對象,涉及到js底層對對象的比較等,有興趣可以再行研究~這裏貼一張結果的圖片

五、利用indexof去重

Es5中提供的方法indexof,根據元素判斷是否在數組中,類似於上面的方法,優點好理解記憶,方法直觀清晰。缺點對ie支持不好(ie:我不背鍋哈)具體可查ie瀏覽器對es5的支持程度,還有速度實際是更慢的,因爲循環和比較相比上面做的多了。

var funcindexof = function(arr) {
  var array = [];
  for (var i = 0; i < arr.length; i++) {
    if (array.indexOf(arr[i]) === -1) {
      array.push(arr[i]);
    }
  }
  return array;
};

六、使用splice去重

嵌套兩層循環,尋找相同元素,如果相同則刪除。

與上個類似,可以理解爲上個方法的不同實現,同樣要考慮到ie對splice的支持,其速度理論上還不如上面的indexof。

var funcsplice = function(arr) {
  for (var i = 0; i < arr.length; i++) {
    for (var j = i + 1; j < arr.length; j++) {
      if (arr[i] == arr[j]) {
        arr.splice(j, 1);
        j--;
      }
    }
  }
  return arr;
};

七、filter結合indexof去重

該方法優勢在於,不輸於第一個方法的簡潔,僅用一行代碼就可處理,巧妙結合filter,indexof等數組方法的特性。不過注意indexof的性能較低,處理幾百個元素還行,如果在性能差的移動端處理上千元素,就會出現阻塞頁面的卡頓。

var funcfilter = function(arr) {
  return arr.filter((item, index) => arr.indexOf(item, 0) === index);
};

相比上面方法五的indexof爲啥少了兩個NaN,原因如下:

 

接下來就是幾個不怎麼常用的方法了,用來在面試官面前裝逼拓寬知識面用的

八、利用includes去重

Includes方法檢測數組是否包含該元素,返回值是true或者false,和indexof有異曲同工之妙~

var funcincludes = function(arr) {
  var array = [];
  for (var i = 0; i < arr.length; i++) {
    if (!array.includes(arr[i])) {
      array.push(arr[i]);
    }
  }
  return array;
};

同樣是對NaN處理的不同,使得includes方法可以去除多餘的NaN,如果有這樣的需求可以利用此方法

這樣看差別應該就很明顯了吧

九、 利用reduce去重

reduce() 是數組的歸併方法,與forEach()、map()、filter()等迭代方法一樣都會對數組每一項進行遍歷,但是reduce() 可同時將前面數組項遍歷產生的結果與當前遍歷項進行運算,這一點是其他迭代方法無法企及的。Reduce函數形式如下

arr.reduce(function(prev,cur,index,arr){
...
}, init); 

其中arr 表示原數組;

prev 表示上一次調用回調時的返回值,或者初始值 init;

cur 表示當前正在處理的數組元素;

index 表示當前正在處理的數組元素的索引,若提供 init 值,則索引爲0,否則索引爲1;

init 表示初始值。

是不是看起來很複雜,但其實實際使用一般只用到prev和cur,分別對應previous上一個返回值和current當前值。

var funcreduce = function(arr) {
  return arr.reduce((prev, cur) => {
    prev.indexOf(cur) === -1 && prev.push(cur);
    return prev;
  }, []);
};

實際上還是如上indexof的變種。

十、reduce和includes結合去重

var funcreduceincludes = function(arr) {
  return arr.reduce((prev, cur) => (prev.includes(cur) ? prev : [...prev, cur]),[]);
};

一行代碼搞定,極客首選之一

本質上還是includes,只不過由reduce幫忙遍歷而已

可以看到結果同上includes

十一、使用hasOwnProperty去重

hasOwnProperty是用來檢測對象是否包含某一屬性的方法。區別於中括號,hasOwnProperty不會對原型鏈上端的對象進行屬性判斷。所以與上面“使用對象屬性不重複去重”略有小小區別(當然別瞎修改原型鏈就沒問題)

var funchasOwnProperty = function(arr) {
  var array = [];
  var temp = {};
  for (var i = 0; i < arr.length; i++) {
    if (!temp.hasOwnProperty(arr[i])) {
      temp[arr[i]] = 1;
      array.push(arr[i]);
    }
  }
  return array;
};

看到與上面對象屬性去重之後結果是一致的。

其實本質上,js在內部聲明某個對象的屬性時,會做一個toString的操作,這也就解釋了爲啥NaN和‘NaN’會被去重,同樣的,如果打印觀察的話,空對象{ }在做對象的屬性名時也會轉化爲"[object Object]"。

 

總結:

總體上來看,去重的思路大概就是三種

一是利用鍵值對裏鍵不重複

二是利用對象裏屬性名不可重複

三則是直接對元素比較判斷

而去重的工具無非就是各種遍歷和查找的方法,在這些方法之上結合上面的思路,也就完成了數組去重。

方法不論好壞,也不僅限於上面有限種類,根據業務需求來實現代碼,纔是最好的數組去重方法~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章