《前端演算法系列》陣列去重

徐小夕發表於2019-06-11

雖然演算法在前端開發中很少會得以使用,但是瞭解常用的演算法,熟悉各種演算法的效能和優劣,將會讓你在前端的道路上走的更遠。

前言

文中所有程式碼位於位於此程式碼倉庫中,大家可以下載程式碼進行學習、推敲和改進。另,如果覺得這些用心推敲的程式碼對你有幫助的話,歡迎 star 一下程式碼倉庫,眾籌博主的小星星。

另外,本文中使用如下函式對程式碼進行效能測試:

module.exports = async function getFnRunTime(fn) {
    let startTime = Date.now(), endTime;
    let result = await fn();
    endTime = Date.now();
    console.log(`total time:${endTime-startTime}ms`,
                'test array\'length:' + len, 
                result.length
    );
}

複製程式碼

為了衡量效能,我們引入程式碼時間複雜度的概念,用大寫O()來表現演算法時間複雜度的記法,我們稱之為大O記法。一般情況下,隨著n的增大,T(n)增長最慢的演算法為最優演算法。

1.雙重for迴圈去重

如果有相同的值則跳過,不相同則push進陣列

function distinct1(arr = testArr) {
    let result = [],
           len = arr && arr.length;
    for (let i=0; i< len; i++) {
        for (let j=i+1; j< len; j++) {
            if (arr[i] === arr[j]) {
                j = ++i;
            }
        }
        result.push(arr[i])
    }
    return result
}
複製程式碼

由於使用了雙層for迴圈,根據程式碼可知時間複雜度為O(n^2),用測試函式測的19815條資料的去重時間為:7-8ms;

2.利用indexOf和forEach/for迴圈去重

function distinct2(arr = testArr) {
    let result = [];
    arr.forEach((v, i, array) => {
        array.indexOf(v, i+1) === -1 && result.push(v)
    });
    return result
}
複製程式碼

可以看到該方法的程式碼很簡潔,時間複雜度為O(n),但是indexOf會進行額外的效能消耗,測試相同的資料耗時為:4-5ms

3.物件法

通過利用物件建名的唯一性去去重

function distinct3(arr = testArr) {
    let result = [], resultObj = {}, len = arr.length;
    for(let i=0; i< len; i++) {
        let val = arr[i],
           type = typeof val;
        if(!resultObj[val]) {
            result.push(val);
            resultObj[val] = [type];
        } else if(!resultObj[val].indexOf(type) < 0) {
            result.push(val);
            resultObj[val] = [type];
        }
    }
    return result
}
複製程式碼

該方法很快,時間複雜度為O(n),但是由於會多建立一個物件,會帶來額外的記憶體開銷,尤其是資料量大的情景下,測試相同的資料耗時為:5ms

4.filter去重方法一

利用filter和indexOf來查詢

function distinct4(arr = testArr) {
    return arr.filter((v, i, array) => array.indexOf(v, i+1) < 0)
}
複製程式碼

該方法也很簡潔,測試相同的資料耗時為:4-5ms,關鍵優化點是利用indexOf的第二個引數去避免不必要的查詢。

5.filter去重方法二

和方法4的區別是利用陣列的索引的唯一性來去重

function distinct5(arr = testArr) {
    return arr.filter((v, i, array) => array.indexOf(v) === i)
}
複製程式碼

該方法同4,但是效能遠不如方法4,因為陣列每次呼叫indexOf都會重新查詢整個陣列,但這是必須要做的操作,否則就不能利用陣列索引的唯一性了。耗時:16ms(小夥伴們都驚呆了)

6.利用es6的set方法

function distinct6(arr = testArr) {
    return [...new Set(arr)]
}
複製程式碼

此方法耗時1ms,但是侷限性很大,針對相同型別的資料很快,但是不同型別的資料去重,將非常慢,這涉及到js相關的底層知識,這裡就先不介紹了,後期需要的話可以專門上一篇文章介紹~

好啦,其實陣列去重有很多種方法,只有你想不到的,沒有實現不了的,如果你有更好更快的方法,歡迎一起交流探討哦~

我們組建的vue-react-小程式-node交流學習群,已經有數百位各企業的前端夥伴加入,歡迎大家關注 趣談前端公眾號,加入前端大家庭,一起探索前端的邊界

《前端演算法系列》陣列去重

相關文章