JS實現數組去重方法整理


前言

我們先來看下面的例子,當然來源與網絡,地址《刪除數組中多個不連續的數組元素的正確姿勢

我們現在將數組中所有的‘ a’ 元素刪除:

var arr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];
arr.forEach(function(value, index) {
    value === 'a' ? arr.splice(index, 1) : '';
})
console.log(arr);
//["a", "b", "c", "d", "a", "e", "g", "f"]

只要相鄰的‘ a’ 元素, 都沒被刪除, splice不但可以刪除元素本身, 還同時可以減少數組長度( 就是抹去一切痕跡),
這樣導致后續的數組元素會代替已經刪除的元素的位置, 但是循環是按照數組的下標按順序刪除, 這樣就會漏掉遷移的元素。

看到網上有網友在說使用delete進行操作,如下:

var arr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];
arr.forEach(function(value, index) {
   value === 'a' ? delete arr[index] : '';
})
console.log(arr); //[2: "b", 3: "c", 4: "d", 7: "e", 8: "g", 10: "f"]

但是得到的arr其實是一個非常規的數組了,也就是說其實delete主要是用於對對象屬性的操作。這確實要根據自己的需求來了。

當然簡單的實現如下:

var arr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];
var newArr = arr.filter(function(key) {
    return key !== 'a'

})
console.log(newArr); //["b", "c", "d", "e", "g", "f"]

 下面總結下常用實現方式。

性能測試模板

let arr1 = Array.from(new Array(100000),(item,index) => {
    return index;
})

let arr2 = Array.from(new Array(50000),(item,index) => {
    return index + index;
})

function distinct(a,b) {
    // 數組去重
}
console.time('去重計算時間');
console.log('去重后的長度', distinct(arr1, arr2).length);
console.timeEnd('去重計算時間');

上面創建了兩個數組長度,1個10W,1個5W長度的數組,合並到一個數組,然后進行去重,驗證下去重的計算時間是多長。

方法一:Array.filter() + indexOf

function distinct(a,b) {
    let arr = a.concat(b);
    return arr.filter((item,index) => {
        return arr.indexOf(item) === index;
    })
}

思路就是ES6 中的 Array.filter() 遍歷數組,並結合 indexOf 來排除重復項。看下結果:

從截圖可以看出,計算時間花費了19753ms的時間。

方法二:使用 for...of + includes()

function distinct(a,b) {
    let arr = a.concat(b)
        let result = []
        for (let i of arr) {
            !result.includes(i) && result.push(i)
        }
        return result
}

這種方式跟Array.filter() + indexOf實現思路差不多,結果如圖所示:

從圖上可以看出,計算的時間跟Array.filter() + indexOf的時間差不多。

方法三:雙重 for 循環

function distinct(a, b) {
    let arr = a.concat(b);
    for (let i=0, len=arr.length; i<len; i++) {
        for (let j=i+1; j<len; j++) {
            if (arr[i] == arr[j]) {
                arr.splice(j, 1);
                // splice 會改變數組長度,所以要將數組長度 len 和下標 j 減一
                len--;
                j--;
            }
        }
    }
    return arr
}

優點:簡單易懂
缺點:占用內存高,速度慢

看下結果:

方法四:Array.sort()

首先使用 sort() 將數組進行排序

然后比較相鄰元素是否相等,從而排除重復項。

 

function distinct(a, b) {
    let arr = a.concat(b)
    arr.sort(function(a,b){        //對數組進行排序才能方便比較
        return a - b;
    })
    let result = [arr[0]]

    for (let i=1, len=arr.length; i<len; i++) {
        arr[i] !== arr[i-1] && result.push(arr[i])
    }
    return result
}

 

從上面代碼可以看出來,做了一次排序和一次循環,所以效率比前面都高,結果如圖所示:

可以看出,結果只花了255ms左右時間。

方法五:for...of + Object

利用對象的屬性不能相同的特點進行去重,代碼如下:

function distinct(a, b) {
    let arr = a.concat(b)
    let result = []
    let obj = {}

    for (let i of arr) {
        if (!obj[i]) {
            result.push(i)
            obj[i] = 1
        }
    }

    return result
}

執行結果如圖所示:

結果只花費了43ms左右時間。

方法六:ES6的new Set()

function distinct(a, b) {
    return Array.from(new Set([...a, ...b]))
}

Set數據結構,它類似於數組,其成員的值都是唯一的。

利用Array.from將Set結構轉換成數組。

結果如圖所示:

從結果看出來該方法執行只花了151ms左右的時間。

總結

從上面幾種方法得出如下結論:

  • 數組去重要么使用for...of + Object方式,要么使用ES6的 new Set()方式。
  • 從執行結果看for...of + Object的效率應該是最高的(只在當前量級的計算結果來看)。

附錄

附錄中添加瀏覽器Array對象支持indexOf和forEach的polyfill:

Array.prototype.indexOf = Array.prototype.indexOf || function(item) {
    for (var i = 0, j = this.length; i < j; i++) {
        if (this[i] === item) {
            return i;
        }
    }
    return -1;
}

Array.prototype.forEach = Array.prototype.forEach || function(callback, thisArg) {
    if (!callback || typeof callback !== 'function') return;

    for (var i = 0, j = this.length; i < j; i++) {
        callback.call(thisArg, this[i], i, this);
    }
}

 


免責聲明!

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



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