JavaScript系列--八種【數組去重】方法的總結

一、前言

數組去重是一個老生常談的問題,但是有時候會彈出點其他東西。

二、雙重循環

這個方法是最常見的,最原始的方法。

// 方法一:雙重循環
var array = [1,1,'1','2','1',1,2]

function unique(arr){
    // res 存結果
    var res = [];
    for(var i = 0, length = arr.length; i < length; i++){
        for(var j = 0, length2 = res.length; j < length2; j++){
            if(arr[i] === res[j]){
                break;
            }
        }
        if(j == length2){
            res.push(arr[i])
        }
    }
    return res;
}

unique(array);  //[1, "1", "2", 2]

思路:雙層循環方法,使用的是循環嵌套,外層是arr,裏層是res,如果arr[i]的值等於res[j]的值,則跳出當前循環,如果都不等於,說明元素唯一,這時候j的值等於res的長度,根據這個判斷,將值添加res中。

優點:兼容性好

缺點:時間複雜度o(n2)

三、indexOf方法

思路:使用indexOf簡化內層循環。

// 方法二:indexOf簡化內層循環
var array = [1,1,'1','2','1',1,2]

function unique(arr){
    // res 存結果
    var res = [];
    for(var i = 0, length = arr.length; i < length; i++){
       var current = arr[i];
       if(res.indexOf(current) === -1){
           res.push(current);
       }
    }
    return res;
}

unique(array);   //[1, "1", "2", 2]

優點:時間複雜度降低

缺點:有的瀏覽器不支持indexOf方法,時間複雜度o(n2)

四、排序後去重

思路:使用快排sort將數組排序,這樣相同的值會被排在一起,只需要判斷當前元素與上一個元素是否相同,相同說明重複,不同就添加進res中。

// 方法三:排序後去重
var array = [1,1,'1','2','1',1,2]

function unique(arr){
    // res 存結果
    var res = [];
    var sortedArray = arr.concat().sort();
    console.log(sortedArray, '-=-=-=-=-=-=')
    var current;
    for(var i = 0, length = sortedArray.length; i < length; i++){
        // 如果是第一個元素 或者是相鄰元素不相同
       if(!i || current !== sortedArray[i]){
           res.push(sortedArray[i]);
       }
       current = sortedArray[i];
    }
    return res;
}

unique(array);   //[1, "1", 1, "2", 2]

優點:已經排好序的數組去重,這種方法效率高於使用 indexOf,時間複雜度o(n)

缺點:已經修改數組的順序,同時存在去重不徹底

注意:sort函數默認排序是 Unicode,srot方法默認可是能給字母實現排序。突然發現了sort在排序的時候存在一個隱式轉換,會把要排序的對象轉換成字符串,sort排序的時候1和'1'是相同的,然後根據unicode比較大小,所以出現了[1, "1", 1, "2", 2]這種情況。

注意:數組進行了 array.concat()操作之後,其實是複製出來一份原有的數組,複製出來的新數組不會影響到原有數組。

五、使用ES5的filter

思路:使用filter簡化外層循環

1、使用indexOf簡化內層循環

// 方法四:filter + indexOf
var array = [1,1,'1','2','1',1,2]

function unique(arr){
    // res 存結果
    var res = arr.filter(function(item, index, arr){
        return arr.indexOf(item) === index;
    })
    return res;
}

unique(array);   //[1, "1", "2", 2]

2、排序去重的方法

// 方法四:filter + sort
var array = [1,1,'1','2','1',1,2]

function unique(arr){
    // res 存結果
    var res = arr.concat().sort().filter(function(item, index, arr){
        return !index ||  item !==arr[index -1]
    })
    return res;
}

unique(array);   //[1, "1", 1, "2", 2]

上面已經講到了sort排序時候存在一個隱式轉換。

優點:很簡潔,思維也比較巧妙,直觀易懂,使用filter簡化外層循環

缺點:不支持 IE9 以下的瀏覽器,時間複雜度o(n*2)

六、Object鍵值對的問題

思路:利用一個空的Object對象,把數組的值存成Object的key,比如就是Object[value] = true;循環判斷的時候,如果Object[value]存在,說明這個值重複了。

// 方法五:Object鍵值對
var array = [1,1,'1','2','1',1,2]

function unique(arr){
    // obj 存對象
    var obj= {};
    var res = arr.filter(function(item, index, arr){
        if(obj.hasOwnProperty(item)) return false;
        else {
            return obj[item] = true;
        }
    })
    return res;
}

unique(array);   //[1, "2"]

然後我們發現1和'1'是不同的,但是這種方法會判斷爲是同一個值,因爲鍵值對中只能是字符串,會進行一個隱式轉換。和sort排序時候的轉換是一樣的,可以通過typeof item+ item,拼成字符串作爲key的值避免這個問題

// 優化
function unique(arr){
    // obj 存對象
    var obj= {};
    var res = arr.filter(function(item, index, arr){
        if(obj.hasOwnProperty(typeof item + item)) return false;
        else {
            return obj[typeof item + item] = true;
        }
    })
    return res;
}

unique(array);   //[1, "1", "2", 2]

優點:hasOwnProperty是對象的屬性存在性檢查方法。對象的屬性可以基於hash表實現,因此對屬性的方法的時間複雜度達到O(1);filter是數組迭代的方法,內部是一個for循環,複雜度O(n)。總的時間複雜度O(n)。

缺點:不兼容IE9以下瀏覽器

七、reduce高階函數

includes() 方法用來判斷一個數組是否包含一個指定的值,根據情況,如果包含則返回 true,否則返回false。

// 方法六:reduce高階函數
var array = [1,1,'1','2','1',1,2]
function unique(arr){
    let newArr = arr.reduce((pre,cur)=>{
        if(!pre.includes(cur)){
            return pre.concat(cur)
        }else{
            return pre
        }
    },[]);
    return newArr;
}
console.log(unique(array));// [1, "1", "2", 2]

優點:高階函數的高級用法。

缺點:兼容性問題,對象數組不能使用includes方法來檢測。includes區分大小寫

八、ES6的Set數據結構

只能說ES6標準越來越好,可以使用Set數據結構,ES6中提供了set數據結構,類似於數組,成員值都是唯一的,沒有重複的。

// 方法七:ES6的set
var array = [1,1,'1','2','1',1,2]

function unique(arr){
    return Array.from(new Set(arr));
}

unique(array);   //[1, "1", "2", 2]

還可以用擴展運算符...

// 優化
var array = [1,1,'1','2','1',1,2]

function unique(arr){
    return  [...new Set(arr)];
}

unique(array);   //[1, "1", "2", 2]

再寫簡單點

// 再優化
var array = [1,1,'1','2','1',1,2]

const unique = arr => [...new Set(arr)];

unique(array);   //[1, "1", "2", 2]

優點:ES6語法簡單高效。

缺點:兼容性問題,加上使用babel編譯不是問題。

九、ES6的Map數據結構

// 方法八:ES6的Map + filter
var array = [1,1,'1','2','1',1,2];

function unique(arr){
    var current = new Map();
    var res = arr.filter(function(item, index, arr){
        if(current.has(item)){
            return false;
        }else{
            return current.set(item, 1);
        }
    })
    return res;
}

unique(array);   //[1, "1", "2", 2]

思路:使用map的方法set和has,用has方法來判斷是否存在這個key,如果沒有值將map中存一個key-value。

注意:最新的ES6規範引入了新的數據類型Map,爲了解決對象中的鍵只能是字符串的問題,其實其他基本數據類型是可以作爲鍵的。

優點:ES6語法簡單高效。

缺點:兼容性問題,加上使用babel編譯不是問題。

十、特殊數據類型比較

var str1 = '1';
var str2 = new String('1');

console.log(str1 == str2); // true
console.log(str1 === str2); // false

console.log(null == null); // true
console.log(null === null); // true

console.log(undefined == undefined); // true
console.log(undefined === undefined); // true

console.log(NaN == NaN); // false
console.log(NaN === NaN); // false

console.log(/a/ == /a/); // false
console.log(/a/ === /a/); // false

console.log({} == {}); // false
console.log({} === {}); // false

看個栗子1

var arr = [1, 2, NaN];
arr.indexOf(NaN); // -1

原因:indexOf底層還是使用 === 來進行判斷的,因爲NAN === NAN結果是false,使用indexOf還是查不到NAN這個元素。

再看個栗子2

function unique(array) {
   return Array.from(new Set(array));
}
console.log(unique([NaN, NaN])) // [NaN]

Set中,NAN === NAN是false,但是這兩個元素是重複的

十一、後話

在對數組去重的性能進行優化,然後想了半天也只寫出來了Object鍵值對的性能,找不到其他既能判斷引用類型,性能又穩定在n^2之內的方式?

自己回答一下:

目前時間複雜度到O(n)的方法:

(1)Object鍵值對,實質是hasOwnProperty的hash表。

(2)ES6的set,map的數據結構

(3)reduce高階函數

【注:我是saucxs,也叫songEagle,鬆寶寫代碼,文章首發於sau交流學習社區(https://www.mwcxs.top),關注我們每天閱讀更多精彩內容】

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