js數組去重方法分析與總結

數組去重常常被人拿來講事,雖然在工做中不經常使用,但他可以很好的考察js基礎知識掌握的深度和廣度,下面從js的不一樣階段總結一下去重的方法。es6

ES3階段

該階段主要經過循環遍歷數組從而達到去重的目的數組

  • 屢次循環去掉重複元素
// 如下全部方法默認都那拿該數組進行測試
var array = [1, 1, '1', '1', null, null, undefined, undefined, new String('1'), new String('1'), /a/, /a/, NaN, NaN,{},{},[],[],{name: 'eric',sex: 'male'},{sex: 'male',name: 'eric'}];
function unique_es3_on2 (arr) {
    var len = arr.length,
        i = 0,
        j,
        flag,
        ret = [];
    for (; i < len ; i++) {
      flag = true;
      for (j = i+1 ; j < len ; j++) {
        if (arr[i] === arr[j]) {
          flag = false;
          break;
        }
      }
      if(flag) {
        ret.push(arr[i]);
      }
    }
    return ret;
}
unique_es3_on2(array)

結果以下圖:
數據結構

可見除了NaN沒有去掉,其餘效果都還挺好。緣由就是NaN===NaN的結果是false。還有就是使用嵌套的循環,時間複雜度高,性能不是很好。性能

  • 藉助新的對象單次循環去掉重複元素
function unique_es3_on(arr) {
    var obj = {},
        i,
        len = arr.length,
        ret = [];
    for(i = 0; i < len ; i++) {
      if(!obj[arr[i]]) {
        obj[arr[i]] = true;
        ret.push(arr[i]);
      }
    }
    return ret;
}

結果以下圖:
測試

雖然時間複雜度不高了,可是效果並很差。由於對象的屬性是字符串,因此會把數組全部元素默認轉化爲字符串,就會產生如下問題:es5

  1. 數值1和字符串'1'以及包裝類型new String('1'),轉化爲字符串之後是相同的會被去掉。
  2. 對象轉化爲字符串之後會被誤判,[]==>'',{}==>'[object Object]',還有就是形式相同,內存地址不一樣的對象會被去除。

爲了解決類型轉換之後出現的問題,能夠用typeof操做符轉一下:code

function unique_es3_on(arr) {
    var obj = {},
        i,
        len = arr.length,
        str,
        ret = [];
    for(i = 0; i < len ; i++) {
      str = typeof arr[i] + arr[i];
      if(!obj[str]) {
        obj[str] = true;
        ret.push(arr[i]);
      }
    }
    return ret;
}

結果如圖:
對象

能夠看到類型轉換的問題基本解決,但對象部分基本都被去除了,由於他們和字符串相加時仍是會發生轉化,解決的方案是把上面的str換成str = typeof arr[i] + JSON.stringify(arr[i]),相加以前先簡單序列化一下。索引

結果如圖: 內存

從以上能夠看出該階段的各類方法或多或少的都有一些問題,該去除的沒去掉,好比NaN。不應去的給去掉了,好比,形式相同但內存地址不一樣的對象(是否應該去掉全看你怎麼定義)。

ES5階段

function unique_es5(arr) {
    return arr.filter(function(ele,index,array) {
      return array.indexOf(ele) == index;
    })
}

結果如圖:

能夠看到除了NaN,其餘表現都是正常的。其中indexOf對於NaN老是返回-1,因此致使誤判。

ES6階段

let unique_includes = (arr) => {
    let newArr = [];
    arr.forEach(function(item){
        if(!newArr.includes(item)){
            newArr.push(item);
        }
    });
    return newArr;
}

結果如圖:

能夠看到結果是符合預期的,es6中數組的擴展方法includes解決了用indexOf的弊端(不夠直觀,結果還要和索引進行比較。對NaN的誤判)。

let unique_set = (arr) => {
    return [...new Set(arr)];
}

結果和includes方法同樣,此處利用es6新增數據結構set的特性,達到去重的目的。

let unique_map = (arr) => {
    let ret = [],
        m = new Map();
    for(val of arr) {
      if(!m.get(val)) {
        m.set(val , true);
        ret.push(val);
      }
    }
    return ret;
}

結果和includes同樣:

此處利用es6新增數據結構map的特性,以前的鍵值對集合(js對象),只能用字符串看成健,map這種數據結構打破了這一限制,各類類型的值均可以看成健,並且map的健是跟內存地址綁定的,只要內存地址不一樣就認爲是不一樣的健,解決了以前形式相同而內存地址不一樣被去掉的問題。對於簡單數據類型,只要嚴格相等就認爲是相同的健,特例NaN也認爲是相同的健。因此就解決了以前的兩個大難題。

總結

雖然只是一個簡單的去重問題,但這一路實踐下來,能夠看到js愈來愈強大,功能也愈來愈完善,同時也愈來愈優雅。如今再聽到有人說,js只是處理簡單表單驗證的玩具車語言之類的云云,我想我也會忍不住在他耳邊懟一句:那他媽是從前。

相關文章
相關標籤/搜索