摘要:將元素作為對(duì)象的鍵,默認(rèn)鍵對(duì)應(yīng)的值為如果對(duì)象中沒(méi)有這個(gè)鍵,則將這個(gè)元素放入結(jié)果數(shù)組中去。
前言
數(shù)組去重在日常開(kāi)發(fā)中的使用頻率還是較高的,也是網(wǎng)上隨便一抓一大把的話(huà)題,所以,我寫(xiě)這篇文章目的在于歸納和總結(jié),既然很多人都在提的數(shù)組去重,自己到底了解多少呢。又或者是如果自己在開(kāi)發(fā)中遇到了去重的需求,自己能想到更好的解決方案嗎。
這次我們來(lái)理一理怎么做數(shù)組去重才能做得最合適,既要考慮兼容性,也要考慮性能和代碼的優(yōu)雅。
我的學(xué)習(xí)路徑是模仿冴羽(github: mqyqingfeng)的學(xué)習(xí)方式,感謝像冴羽這樣優(yōu)秀的人在前面領(lǐng)跑,我不想光看不做,所以多實(shí)踐多輸出,希望未來(lái)能走出我自己的路。
一、入門(mén)方案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]; // 如果在結(jié)果數(shù)組循環(huán)中找到了該元素,則跳出循環(huán),進(jìn)入下一個(gè)源數(shù)組元素的判斷 if(resultItem === arrayItem) { break; } } // 如果把結(jié)果數(shù)組循環(huán)完都沒(méi)有找到該元素,就將該元素壓入結(jié)果數(shù)組中 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, ""]
以上代碼是最簡(jiǎn)單實(shí)現(xiàn)數(shù)組去重的方式,優(yōu)點(diǎn)在于兼容性極好,缺點(diǎn)就是兩次 for 循環(huán),時(shí)間復(fù)雜度為 O(n^2),性能較差。
二、數(shù)組的 indexOf 屬性數(shù)組中的 indexOf 屬性是 ES5 的規(guī)范,只有 IE8 及更早版本不支持該方法。相對(duì)來(lái)說(shuō),如果你不需要兼容 IE8 的話(huà),盡量用 indexOf 來(lái)判斷一個(gè)元素是否在數(shù)組中。
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; }三、數(shù)組的 filter 屬性
數(shù)組的 filter() 方法創(chuàng)建一個(gè)新的數(shù)組,新數(shù)組中的元素是通過(guò)檢查指定數(shù)組中符合條件的所有元素。
filter 的回調(diào)有三個(gè)參數(shù),其中第三個(gè)參數(shù)是當(dāng)前元素屬于的數(shù)組對(duì)象,這樣我們可以繼續(xù)利用 indexOf 屬性啦。
function unique(origin) { var result = origin.filter(function (item, index, array){ // 獲取元素在源數(shù)組的位置,只返回那些索引等于當(dāng)前元素索引的值。 return array.indexOf(item) === index; }); return result; }
filter 兼容到 IE9, 這種方法沒(méi)有 for 循環(huán),主要利用了 filter 和 indexOf 屬性,所以代碼相對(duì)比較優(yōu)雅。
四、利用 Object 的 key valuefunction unique(origin) { var result = []; var hashTable = {}; for(var i = 0; i< origin.length; i++) { // 如果鍵對(duì)應(yīng)的值,為真,意味著對(duì)象的鍵中已經(jīng)有重復(fù)的鍵了。 if(!hashTable[origin[i]]) { // 將元素作為對(duì)象的鍵,默認(rèn)鍵對(duì)應(yīng)的值為 true, hashTable[origin[i]] = true; // 如果對(duì)象中沒(méi)有這個(gè)鍵,則將這個(gè)元素放入結(jié)果數(shù)組中去。 result.push(origin[i]); } } return result; }
這種方案的事件復(fù)雜度為 O(n), 但是對(duì)象的鍵,默認(rèn)是字符串類(lèi)型,這意味著什么呢,數(shù)字 1 和 字符串 "1",在鍵中是相等的,所以,上面這種方法不適合字符串和數(shù)字混合的去重。
所以我們將元素的類(lèi)型也放入對(duì)象的鍵中:
function unique(origin) { var result = []; var hashTable = {}; for(var i = 0; i< origin.length; i++) { var current = origin[i]; // 字符串拼接元素的類(lèi)型和元素 var key = typeof(current) + current; if(!hashTable[key]) { hashTable[key] = true; result.push(current); } } return result; }五、數(shù)組的 sort 方法
function unique(origin) { return origin.concat.sort().filter(function(item, index, array) { // !index 表示第 0 個(gè)元素應(yīng)該被返回。 return !index || item !== origin[index-1] }) } function unique(array) { array.sort(); // 排序字符串 array.sort(function(a, b) { return a-b; // 排序數(shù)字 }) for(let i=0; isort 方法的優(yōu)點(diǎn)在于利用了排序,返回后一個(gè)和前一個(gè)不相等的元素。比較簡(jiǎn)潔和直觀。缺點(diǎn)在于改變了元素的本來(lái)的排序位置。
六、ES6 SetES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set,它類(lèi)似于數(shù)組,但是成員的值都是唯一的,沒(méi)有重復(fù)的值。向 Set 加入值的時(shí)候,不會(huì)發(fā)生類(lèi)型轉(zhuǎn)變,所以 5 和 "5" 是兩個(gè)不同的值。Set內(nèi)部判斷兩個(gè)值是否相同,用的是類(lèi)似于 "==="的算法,但是區(qū)別是,在set內(nèi)部認(rèn)為NaN 等于 NaN ;
Set 可以轉(zhuǎn)換為數(shù)組,所以很容易實(shí)現(xiàn)去重
function unique(origin) { return Array.from(new Set(origin)); }七、ES6 MapES6 新增了 Map 數(shù)據(jù)結(jié)果,通過(guò) has 和 set 方法就能很方便的對(duì)前面的 object key value 方案進(jìn)行優(yōu)化。
function unique(origin){ const map = new Map() return origin.filter((item) => !map.has(item) && map.set(item, true)) }八、類(lèi)型判斷一些常見(jiàn)的數(shù)據(jù)類(lèi)型是 === 和 indexOf 是無(wú)法檢測(cè)的,舉個(gè)例子:
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所以在判斷的時(shí)候,如果數(shù)據(jù)里有 NaN 和對(duì)象時(shí)要避免使用 indexOf 和 ===;
前面 Set 那里說(shuō)過(guò)了,所以 Set 方法是可以去重 NaN的。
總結(jié)數(shù)據(jù)去重在網(wǎng)上已經(jīng)看煩了,但還是想專(zhuān)門(mén)寫(xiě)一篇文章來(lái)實(shí)踐和總結(jié),能在工作中多幾個(gè)思路也是極好的。感謝那些熱愛(ài)分享和喜歡輸出的人。
歡迎關(guān)注我的個(gè)人公眾號(hào)“謝南波”,專(zhuān)注分享原創(chuàng)文章。
掘金專(zhuān)欄 JavaScript 系列文章JavaScript之變量及作用域
JavaScript之聲明提升
JavaScript之執(zhí)行上下文
JavaScript之變量對(duì)象
JavaScript之原型與原型鏈
JavaScript之作用域鏈
JavaScript之閉包
JavaScript之this
JavaScript之a(chǎn)rguments
JavaScript之按值傳遞
JavaScript之例題中徹底理解this
JavaScript專(zhuān)題之模擬實(shí)現(xiàn)call和apply
JavaScript專(zhuān)題之模擬實(shí)現(xiàn)bind
JavaScript專(zhuān)題之模擬實(shí)現(xiàn)new
JS專(zhuān)題之事件模型
JS專(zhuān)題之事件循環(huán)
JS專(zhuān)題之去抖函數(shù)
JS專(zhuān)題之節(jié)流函數(shù)
JS專(zhuān)題之函數(shù)柯里化
JS專(zhuān)題之?dāng)?shù)組去重
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/108916.html
摘要:根據(jù)需求的特點(diǎn),數(shù)組展開(kāi)需要進(jìn)行迭代和遞歸?;卮鹞恼麻_(kāi)頭的問(wèn)題將多重?cái)?shù)組轉(zhuǎn)化成單層數(shù)組的過(guò)程就是數(shù)組展開(kāi),也叫作數(shù)組扁平化一循環(huán)加遞歸最簡(jiǎn)單的思路循環(huán)中判斷,如果子元素是數(shù)組則遞歸。 前言 首先什么是數(shù)組展開(kāi)? 假如現(xiàn)在有這樣一個(gè)需求:將后臺(tái)的一個(gè)多重 List 數(shù)據(jù),展開(kāi)成一個(gè) List 后,并去重后排序; [a, b, [c, d], [[d],e], f] => [a, b, ...
摘要:專(zhuān)題系列共計(jì)篇,主要研究日常開(kāi)發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖節(jié)流去重類(lèi)型判斷拷貝最值扁平柯里遞歸亂序排序等,特點(diǎn)是研究專(zhuān)題之函數(shù)組合專(zhuān)題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實(shí)現(xiàn)模式需求我們需要寫(xiě)一個(gè)函數(shù),輸入,返回。 JavaScript 專(zhuān)題之從零實(shí)現(xiàn) jQuery 的 extend JavaScritp 專(zhuān)題系列第七篇,講解如何從零實(shí)現(xiàn)一個(gè) jQuery 的 ext...
距離上次發(fā)文,已經(jīng)有一段時(shí)間了,最近工作比較忙,這不眼看快雙十一了,就相當(dāng)于給大家一些福利吧! showImg(https://segmentfault.com/img/remote/1460000016538082?w=250&h=250); 一、什么是數(shù)組去重 簡(jiǎn)單說(shuō)就是把數(shù)組中重復(fù)的項(xiàng)刪除掉,你 GET 到了嗎 ?下面我將簡(jiǎn)單介紹下幾種基本的方法及其優(yōu)缺點(diǎn)。 二、方法匯總 兩層循環(huán) 無(wú)相同...
摘要:寫(xiě)在前面專(zhuān)題系列是我寫(xiě)的第二個(gè)系列,第一個(gè)系列是深入系列。專(zhuān)題系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫(xiě)在前面 JavaScript 專(zhuān)題系列是我寫(xiě)的第二個(gè)系列,第一個(gè)系列是 JavaScript 深入系列。 JavaScript 專(zhuān)題系列共計(jì) 20 篇,主要研究日常開(kāi)發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖、節(jié)流、去重、類(lèi)型判斷、拷貝、最值、扁平、柯里...
摘要:專(zhuān)題系列第三篇,講解各種數(shù)組去重方法,并且跟著寫(xiě)一個(gè)前言數(shù)組去重方法老生常談,既然是常談,我也來(lái)談?wù)?。它?lèi)似于數(shù)組,但是成員的值都是唯一的,沒(méi)有重復(fù)的值。 JavaScript 專(zhuān)題系列第三篇,講解各種數(shù)組去重方法,并且跟著 underscore 寫(xiě)一個(gè) unique API 前言 數(shù)組去重方法老生常談,既然是常談,我也來(lái)談?wù)劇?雙層循環(huán) 也許我們首先想到的是使用 indexOf 來(lái)循...
閱讀 2586·2021-11-25 09:43
閱讀 1863·2021-09-22 15:26
閱讀 3742·2019-08-30 15:56
閱讀 1714·2019-08-30 15:55
閱讀 1899·2019-08-30 15:54
閱讀 816·2019-08-30 15:52
閱讀 3158·2019-08-29 16:23
閱讀 897·2019-08-29 12:43