js數組去重的方法


1.如果不借助任何方法,初級寫法應該是:

function unique(arr){
    var res=[];
    for(var i=0,len=arr.length;i<len;i++){
        var obj = arr[i];
        for(var j=0,jlen = res.length;j<jlen;j++){
            if(res[j]===obj) break;            
        }
        if(jlen===j)res.push(obj);
    }
    return res;
}
var arr=[1,1,'2','1',3,4]
arr = unique(arr);
console.log(arr);

 

2.如果不考慮兼容性,會用ES5里面數組的indexOf()方法。

function unique(arr){
    var res =[];
    for(var i=0,len=arr.length;i<len;i++){
        var obj = arr[i];
        if(res.indexOf(obj)===-1) res.push(obj);
    }
    return res;
}
var arr=[1,1,'2','1',3,4]
arr = unique(arr);
console.log(arr);// arr=[1,'2','1',3,4]

3.利用ES5數組里的filter過濾:

function unique(a) {
 
  var res = a.filter(function(item, index, array) {
    return array.indexOf(item) === index;
  });
  
  return res;
}
 
 
var a = [1, 1, '1', '2', 1];
var ans = unique(a);
console.log(ans); 

 

方法二

 

法一是將原數組中的元素和結果數組中的元素一一比較,我們可以換個思路,將原數組中重復元素的最后一個元素放入結果數組中。

 

function unique(a) {

  var res = [];

 

  for (var i = 0, len = a.length; i < len; i++) {

    for (var j = i + 1; j < len; j++) {

      // 這一步十分巧妙

      // 如果發現相同元素

      // 則 i 自增進入下一個循環比較

      if (a[i] === a[j])

        j = ++i;

    }

 

    res.push(a[i]);

  }

 

  return res;

}

 

 

var a = [1, 1, '1', '2', 1];

var ans = unique(a);

console.log(ans); // => ["1", "2", 1]

 

雖然復雜度還是 O(n^2),但是可以看到結果不同,1 出現在了數組最后面,因為結果數組取的是元素最后一次出現的位置。

 

方法三(sort)

 

如果筆試面試時只答出了上面這樣 O(n^2) 的方案,可能還不能使面試官滿意,下面就來說幾種進階方案。

 

將數組用 sort 排序后,理論上相同的元素會被放在相鄰的位置,那么比較前后位置的元素就可以了。

 

function unique(a) {

  return a.concat().sort().filter(function(item, pos, ary) {

    return !pos || item != ary[pos - 1];

  });

}

 

 

var a = [1, 1, 3, 2, 1, 2, 4];

var ans = unique(a);

console.log(ans); // => [1, 2, 3, 4]

 

但是問題又來了,1 和 "1" 會被排在一起,不同的 Object 會被排在一起,因為它們 toString() 的結果相同,所以會出現這樣的錯誤:

 

var a = [1, 1, 3, 2, 1, 2, 4, '1'];

var ans = unique(a);

console.log(ans); // => [1, 2, 3, 4]

 

當然你完全可以針對數組中可能出現的不同類型,來寫這個比較函數。不過這似乎有點麻煩。

 

方法四 (object)

 

用 JavaScript 中的 Object 對象來當做哈希表,這也是幾年前筆試時的解法,跟 sort 一樣,可以去重完全由 Number 基本類型組成的數組。

 

function unique(a) {

  var seen = {};

 

  return a.filter(function(item) {

    return seen.hasOwnProperty(item) ? false : (seen[item] = true);

  });

}

 

 

var a = [1, 1, 3, 2, 1, 2, 4];

var ans = unique(a);

console.log(ans); // => [1, 3, 2, 4]

 

還是和方法三一樣的問題,因為 Object 的 key 值都是 String 類型,所以對於 1 和 "1" 無法分別,我們可以稍微改進下,將類型也存入 key 中。

 

function unique(a) {

  var ret = [];

  var hash = {};

 

  for (var i = 0, len = a.length; i < len; i++) {

    var item = a[i];

 

    var key = typeof(item) + item;

 

    if (hash[key] !== 1) {

      ret.push(item);

      hash[key] = 1;

    }

  }

 

  return ret;

}

 

 

var a = [1, 1, 3, 2, '4', 1, 2, 4, '1'];

var ans = unique(a);

console.log(ans); // => [1, 3, 2, "4", 4, "1"]

 

雖然解決了討厭的 1 和 "1" 的問題,但是還有別的問題!

 

var a = [{name: "hanzichi"}, {age: 30}, new String(1), new Number(1)];

var ans = unique(a);

console.log(ans); // => [Object, String]

 

但是如果數組元素全部是基礎類型的 Number 值,鍵值對法應該是最高效的!

 

方法五 (ES6)

 

ES6 部署了 Set 以及 Array.from 方法,太強大了!如果瀏覽器支持,完全可以這樣:

 

function unique(a) {

  return Array.from(new Set(a));

}

 

var a = [{name: "hanzichi"}, {age: 30}, new String(1), new Number(1)];

var ans = unique(a);

console.log(ans); // => [Object, Object, String, Number]

 

_.unique

 

最后來看看 underscore 對此的實現方式,underscore 將此封裝到了 _.unique 方法中,調用方式為 _.unique(array, [isSorted], [iteratee])。其中第一個參數是必須的,是需要去重的數組,第二個參數可選,如果數組有序,則可以傳入布爾值 true,第三個參數可選,如果需要對數組迭代的結果去重,則可以傳入一個迭代函數。而數組元素去重是基於 === 運算符的。

 

其實很簡單,underscore 中的實現方式和上面的方法一相似。

 

我們來看它的核心代碼:

 

for (var i = 0, length = getLength(array); i  length; i++) {

  var value = array[i],

      // 如果指定了迭代函數

      // 則對數組每一個元素進行迭代

      computed = iteratee ? iteratee(value, i, array) : value;

 

  // 如果是有序數組,則當前元素只需跟上一個元素對比即可

  // 用 seen 變量保存上一個元素

  if (isSorted) {

    // 如果 i === 0,則直接 push

    // 否則比較當前元素是否和前一個元素相等

    if (!i || seen !== computed) result.push(value);

    // seen 保存當前元素,供下一次對比

    seen = computed;

  } else if (iteratee) {

    // 如果 seen[] 中沒有 computed 這個元素值

    if (!_.contains(seen, computed)) {

      seen.push(computed);

      result.push(value);

    }

  } else if (!_.contains(result, value)) {  

    // 如果不用經過迭代函數計算,也就不用 seen[] 變量了

    result.push(value);

  }

}

 

外面的循環遍歷數組元素,對於每個元素,如果數組有序,則和前一個元素比較,如果相同,則已經出現過,不加入到結果數組中,否則則加入。而如果有迭代函數,則計算傳入迭代函數后的值,對值去重,調用 .contains 方法,而該方法的核心就是調用 .indexOf 方法,和我們上面說的方法一異曲同工。

 

關於 _.unique 方法的詳細代碼,可以參考 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L519-L547

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM