數組去重在平常開發中的使用頻率仍是較高的,也是網上隨便一抓一大把的話題,因此,我寫這篇文章目的在於概括和總結,既然不少人都在提的數組去重,本身到底瞭解多少呢。又或者是若是本身在開發中遇到了去重的需求,本身能想到更好的解決方案嗎。javascript
此次咱們來理一理怎麼作數組去重才能作得最合適,既要考慮兼容性,也要考慮性能和代碼的優雅。java
個人學習路徑是模仿冴羽(github: mqyqingfeng)的學習方式,感謝像冴羽這樣優秀的人在前面領跑,我不想光看不作,因此多實踐多輸出,但願將來能走出我本身的路。git
function unique(origin) { var result = []; for(var i = 0; i < origin.length; i++) { var arrayItem = origin[i]; for(var j= 0; j< result.length; j++) { var resultItem = result[j]; // 若是在結果數組循環中找到了該元素,則跳出循環,進入下一個源數組元素的判斷 if(resultItem === arrayItem) { break; } } // 若是把結果數組循環完都沒有找到該元素,就將該元素壓入結果數組中 if(j === result.length) { result.push(arrayItem); } } return result; } var array = ['a', 'b', 'c', '1', 0, 'c', 1, '', 1, 0]; console.log(unique(array)); // ["a", "b", "c", "1", 0, 1, ""]
以上代碼是最簡單實現數組去重的方式,優勢在於兼容性極好,缺點就是兩次 for 循環,時間複雜度爲 O(n^2),性能較差。github
數組中的 indexOf 屬性是 ES5 的規範,只有 IE8 及更早版本不支持該方法。相對來講,若是你不須要兼容 IE8 的話,儘可能用 indexOf 來判斷一個元素是否在數組中。算法
function unique(origin){ var result = []; for(var i = 0; i< origin.length; i++) { var item = origin[i]; if(result.indexOf(item) === -1) { result.push(item); } } return result; }
數組的 filter() 方法建立一個新的數組,新數組中的元素是經過檢查指定數組中符合條件的全部元素。
filter 的回調有三個參數,其中第三個參數是當前元素屬於的數組對象,這樣咱們能夠繼續利用 indexOf 屬性啦。數組
function unique(origin) { var result = origin.filter(function (item, index, array){ // 獲取元素在源數組的位置,只返回那些索引等於當前元素索引的值。 return array.indexOf(item) === index; }); return result; }
filter 兼容到 IE9, 這種方法沒有 for 循環,主要利用了 filter 和 indexOf 屬性,因此代碼相對比較優雅。數據結構
function unique(origin) { var result = []; var hashTable = {}; for(var i = 0; i< origin.length; i++) { // 若是鍵對應的值,爲真,意味着對象的鍵中已經有重複的鍵了。 if(!hashTable[origin[i]]) { // 將元素做爲對象的鍵,默認鍵對應的值爲 true, hashTable[origin[i]] = true; // 若是對象中沒有這個鍵,則將這個元素放入結果數組中去。 result.push(origin[i]); } } return result; }
這種方案的事件複雜度爲 O(n), 可是對象的鍵,默認是字符串類型,這意味着什麼呢,數字 1 和 字符串 '1',在鍵中是相等的,因此,上面這種方法不適合字符串和數字混合的去重。閉包
因此咱們將元素的類型也放入對象的鍵中:app
function unique(origin) { var result = []; var hashTable = {}; for(var i = 0; i< origin.length; i++) { var current = origin[i]; // 字符串拼接元素的類型和元素 var key = typeof(current) + current; if(!hashTable[key]) { hashTable[key] = true; result.push(current); } } return result; }
function unique(origin) { return origin.concat.sort().filter(function(item, index, array) { // !index 表示第 0 個元素應該被返回。 return !index || item !== origin[index-1] }) } function unique(array) { array.sort(); // 排序字符串 array.sort(function(a, b) { return a-b; // 排序數字 }) for(let i=0; i<array.length; i++) { if(array[i] === array[i+1]) { array.splice(i, 1); i--; // 應該將前一個數刪除,而不是刪除後一個數。是由於元素被刪除以後,後面元素的索引會遷移,因此要 i--; } } return array; }
sort 方法的優勢在於利用了排序,返回後一個和前一個不相等的元素。比較簡潔和直觀。缺點在於改變了元素的原本的排序位置。函數
ES6 提供了新的數據結構 Set,它相似於數組,可是成員的值都是惟一的,沒有重複的值。向 Set 加入值的時候,不會發生類型轉變,因此 5 和 '5' 是兩個不一樣的值。Set內部判斷兩個值是否相同,用的是相似於 "==="的算法,可是區別是,在set內部認爲NaN 等於 NaN ;
Set 能夠轉換爲數組,因此很容易實現去重
function unique(origin) { return Array.from(new Set(origin)); }
ES6 新增了 Map 數據結果,經過 has 和 set 方法就能很方便的對前面的 object key value 方案進行優化。
function unique(origin){ const map = new Map() return origin.filter((item) => !map.has(item) && map.set(item, true)) }
一些常見的數據類型是 ===
和 indexOf
是沒法檢測的,舉個例子:
console.log({} === {}) // false; console.log(NaN === NaN) // false; console.log(/a/ === /a/); // false; console.log(1 === new String('1')) // false; var arr = [NaN]; console.log(arr.indexOf(NaN)); // -1
因此在判斷的時候,若是數據裏有 NaN 和對象時要避免使用 indexOf
和 ===
;
前面 Set 那裏說過了,因此 Set 方法是能夠去重 NaN的。
數據去重在網上已經看煩了,但仍是想專門寫一篇文章來實踐和總結,能在工做中多幾個思路也是極好的。感謝那些熱愛分享和喜歡輸出的人。
歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。