JavaScript實現(xiàn)數(shù)組去重的14種方法大全
一、利用for循環(huán)嵌套去重
var array = [ 0, 0, 1, 1, '1', '1', 'true', 'true', true, true, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 'a', 'a', {}, {}, { a: 1 }, { b: 2 }, { a: 1 }]; function unique(array) { // res用來存儲結(jié)果 var res = []; for (var i = 0, arrayLen = array.length; i < arrayLen; i++) { for (var j = 0, resLen = res.length; j < resLen; j++ ) { if (array[i] === res[j]) { break; } } // 如果array[i]是唯一的,那么執(zhí)行完循環(huán),j等于resLen if (j === resLen) { res.push(array[i]) } } return res; } // NaN 和 對象 無法去重 console.log(unique(array)); // [0, 1, '1', 'true', true, false, undefined, null, NaN, NaN, 'NaN', 'a', {…}, {…}, {…}, {…}, {…}]
輸出結(jié)果:NaN 和 對象 無法去重,其他都可以去重(包括null)
我的結(jié)論:for循環(huán) 判斷依據(jù)是全等===——值和類型都相同,才去重
二、利用splice() + for循環(huán)嵌套(ES5中最常用)
利用for循環(huán)嵌套,然后splice去重:將數(shù)組中的每一個元素依次與其他元素作比較,發(fā)現(xiàn)重復(fù)元素,刪除。
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(arr) { for (var i = 0; i < arr.length; i++) { for (var j = i + 1; j < arr.length; j++) { if (arr[i] == arr[j]) { //如果第一個等同于第二個,splice方法刪除第二個 arr.splice(j, 1); j--; } } } return arr; } console.log(arr[4],typeof arr[4],arr[6],typeof arr[6]) // true string true boolean console.log(arr[4]==1,arr[6]==1) // false true // NaN 和 {} 沒有去重,且兩個 null 直接消失了 // 如果第一個等同于第二個,splice方法刪除第二個,所以1跟'1'、true留1,false跟0留false console.log(unique(arr)); // [1, 2, 'true', 15, false, undefined, NaN, NaN, 'NaN', 'a', {…}, {…}, {…}, {…}, {…}]
雙層循環(huán),外層循環(huán)元素,內(nèi)層循環(huán)時比較值。值相同時,則刪去這個值。
輸出結(jié)果:NaN 和 對象 無法去重,且null會被忽略掉,直接消失了
我的結(jié)論:splice() 判斷依據(jù)是值等==——值相同
字符串類型的數(shù)字、boolean類型:true、false,比較時會轉(zhuǎn)為number類型的數(shù)字
三、利用indexOf() + for循環(huán)去重
indexOf() 方法可返回數(shù)組中某個指定的元素位置。
該方法將從頭到尾地檢索數(shù)組,看它是否含有對應(yīng)的元素。開始檢索的位置在數(shù)組 start 處或數(shù)組的開頭(沒有指定 start 參數(shù)時)。如果找到一個 item,則返回 item 的第一次出現(xiàn)的位置。開始位置的索引為 0。
如果在數(shù)組中沒找到指定元素則返回 -1。
提示:如果你想查找字符串最后出現(xiàn)的位置,請使用 lastIndexOf() 方法。
新建一個空的結(jié)果數(shù)組,for 循環(huán)原數(shù)組,判斷結(jié)果數(shù)組是否存在當(dāng)前元素,如果有相同的值則跳過,不相同則push進(jìn)數(shù)組。
輸出結(jié)果:NaN 和 對象 無法去重,indexOf() 方法無法識別數(shù)組的 NaN 和 {} 成員。
我的結(jié)論:indexOf() 判斷依據(jù)是全等===——值和類型都相同,才去重
四、利用sort() + for循環(huán)去重
sort() 方法用于對數(shù)組的元素進(jìn)行排序。
排序順序可以是字母或數(shù)字,并按升序或降序。
? 默認(rèn)排序順序為按字母升序。
注意:當(dāng)數(shù)字是按字母順序排列時"40"將排在"5"前面。
? 使用數(shù)字排序,你必須通過一個函數(shù)作為參數(shù)來調(diào)用。
函數(shù)指定數(shù)字是按照升序還是降序排列。
注意: 這種方法會改變原始數(shù)組!
利用sort()排序方法,然后根據(jù)排序后的結(jié)果進(jìn)行遍歷及相鄰元素比對。
var array = [1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(array) { if(!Array.isArray(array)) { console.log('type error'); return; } arr = array.sort(); // 對array進(jìn)行排序,sort()會改變原數(shù)組 var arrys = [arr[0]]; // 新建一個數(shù)組,用于存放不包含重復(fù)元素的數(shù)組 for(var i = 1; i < arr.length; i++) { if(arr[i] != arr[i-1]) { arrys.push(arr[i]); } } return arrys; } // NaN 和 對象 無法去重,其他都可以去重(包括null) console.log(unique(array)); // [0, 1, 15, 2, NaN, NaN, 'NaN', {…}, {…}, {…}, {…}, {…}, 'a', false, null, 'true', true, undefined]
輸出結(jié)果:NaN 和 對象 無法去重,其他都可以去重(包括null)
我的結(jié)論:sort() 判斷依據(jù)是全等===——值和類型都相同,才去重
五、利用includes() + for循環(huán)去重(ES7)
includes() 方法用來判斷一個數(shù)組是否包含一個指定的值,如果是返回 true,否則false。
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(arr) { if (!Array.isArray(arr)) { console.log("type error!"); return; } var array = []; for (var i = 0; i < arr.length; i++) { if (!array.includes(arr[i])) { // includes 檢測數(shù)組是否有某個值 array.push(arr[i]); } } return array; } // 對象 無法去重,其他都可以去重(包括NaN、null) console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]
輸出結(jié)果:對象 無法去重,其他都可以去重(包括NaN、null)
我的結(jié)論:includes() 判斷依據(jù)是全等===——值和類型都相同,才去重
PS:JavaScript String 對象也有 includes() 方法。
includes() 方法用于判斷字符串是否包含指定的子字符串。如果找到匹配的字符串則返回 true,否則返回 false。
注意:JavaScript String 對象的 includes() 方法區(qū)分大小寫。
六、利用Object鍵值對及其hasOwnProperty()去重
? hasOwnProperty方法
hasOwnProperty() 方法是 Object 的原型方法(也稱實例方法),它定義在 Object.prototype 對象之上,所有 Object 的實例對象都會繼承 hasOwnProperty() 方法。
hasOwnProperty() 方法用來檢測一個屬性是否是對象的自有屬性,而不是從原型鏈繼承的。如果該屬性是自有屬性,那么返回 true,否則返回 false。
換句話說,hasOwnProperty() 方法不會檢測對象的原型鏈,只會檢測當(dāng)前對象本身,只有當(dāng)前對象本身存在該屬性時才返回true。
用法:object.hasOwnProperty(propertyName) ,參數(shù)propertyName指要檢測的屬性名;
? Object 鍵值對:var obj = {...}
—— 如何實現(xiàn)去重 ——
1、通過 filter 為數(shù)組的每一個元素做條件過濾。
2、再通過 三元運算 將數(shù)組的元素作為key登記在obj中,將它對應(yīng)的值設(shè)為true。
3、通過 hasOwnProperty 判斷對象是否包含某一屬性(鍵),如果包含即返回false, filter 接收到false將會過濾掉該 item (array[index]) 的return事件。
Object 鍵值對這種方法是利用一個空的 Object 對象,我們把數(shù)組的值存成 Object 的 key 值,比如 Object[value1] = true,在判斷另一個值的時候,如果 Object[value2]存在的話,就說明該值是重復(fù)的。示例代碼如下:
var array = [1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(array) { var obj = {}; return array.filter(function(item, index, array){ return obj.hasOwnProperty(item) ? false : (obj[item] = true) }) } // 可去重同值不同類型的項,但對象 無法去重,且原數(shù)組中除第一個對象外,所有對象都會被去(忽略)掉; console.log(unique(array)); // [1, 2, 'true', 15, false, undefined, null, NaN, 0, 'a', {…}]
輸出結(jié)果:Object 鍵值對方法的有2個bug:還會去重同值不同類型的項,且無法去重(區(qū)分)多個對象,即原數(shù)組中除第一個對象可以去重外,所有對象都會被去(忽略)掉,
缺點總結(jié):
bug - 1 會去重同值不同類型的項
由于Object的鍵只能是String類型,因此 obj[1] 與 obj['1'] 以及 obj['true'] 與 obj[true]、obj[NaN] 與 obj['NaN'] 是等價的,它們引用同一個堆棧,最終在第二數(shù)組實參中 1 和 '1' 、'true' 和 true、NaN 和 'NaN' 被去重為同一個元素。
bug -2 原數(shù)組中除第一個對象可以去重外,所有對象都會被去(忽略)掉 ——無法去重多個對象
改進(jìn)1.0:typeOf - 升級版
分析:我們可以發(fā)現(xiàn),由于Object的鍵只能是String類型,這樣做是有問題的,因為 1 和 '1' 是不同的,但是這種方法會判斷為同一個值,這是因為對象的鍵值只能是字符串,所以我們可以通過使用 typeof item + item 的方式拼成一個字符串,來作為obj的 key 值來避免這個問題:
var arr = [1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{}]; function unique(arr) { var obj = {}; return arr.filter(function (item, index, arr) { // 使用typeof item+item拼成一個字符串,來作為 hasOwnProperty 的判斷依據(jù)以及obj的key值 return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true); }); } // 都可以去重,包括NaN、null、{}(僅限數(shù)組中的一個對象,如有其他對象都會被去掉) console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}]
輸出結(jié)果:升級版存在1個bug:無法去重(區(qū)分)多個對象,即原數(shù)組中除第一個對象可以去重外,所有對象都會被去(忽略)掉;
缺點總結(jié):無法去重(區(qū)分)多個對象
bug - 對象去重,僅限原數(shù)組中的第一個對象,其他所有對象都會被去(忽略)掉;
如果數(shù)組中存在多個對象,除了數(shù)組中的第一個對象,所有的對象都會被過濾(不管該對象是否重復(fù)都會被去掉)。
——分析去重過程 ——
使用filter函數(shù),過濾出一個新數(shù)組,也就是最終的去重后的數(shù)組。
三元表達(dá)式,主要是為了得到判斷結(jié)果:true或false,然后return這個true或false這個結(jié)果,以便filter函數(shù)過濾出一個不包含重復(fù)項的新數(shù)組
① 當(dāng)obj.hasOwnProperty(typeof item + item)為 false 時,說明obj對象中不存在(typeof item + item)這個自有屬性,也就是說當(dāng)前循環(huán)的arr中的item項的值,沒有讓typeof item + item成為obj的私有屬性,obj不存在(typeof item + item)這個屬性
此時,整個三元表達(dá)式的值為(obj[typeof item + item] = true),整個值的真假(true 或 flase)決定了filter函數(shù)當(dāng)前循環(huán)的item值是否能成為filter過濾出的新數(shù)組的元素。
因為目前obj中不存在(typeof item + item)這個自有屬性,所以obj[ typeof item + item ] =undefined,所以undefined = true為true,說明當(dāng)前循環(huán)的arr中的item項,符合條件,會加入filter函數(shù)過濾后的新數(shù)組
② 當(dāng)obj.hasOwnProperty(typeof item + item)為 true 時,說明obj對象中已存在(typeof item + item)這個自有屬性,
此時,三元表達(dá)式的值為false,說明當(dāng)前循環(huán)的arr中的item項,不符合條件,不會加入filter函數(shù)過濾后的新數(shù)組
舉個例子:array[0]和array[4]分別被 typeof 拼接成 "number1" 和 "stringtrue",很好的區(qū)分了不同類型轉(zhuǎn)字符串后的區(qū)別。
但是如果數(shù)組中存在對象,比如[ { name: 97 }, { descript: 'z' } ],由于 typeof item + item 的結(jié)果都會是 object[object Object] (String類型),所以除了數(shù)組中的第一個對象,所有的對象都會被過濾(不重復(fù)的對象也會被去掉)
改進(jìn)2.0 :JSON.stringify() - 終極解決方案!
然而,即便如此,我們依然無法正確區(qū)分出兩個對象,比如 {value: 1} 和 {value: 2},因為 object[typeof item + item] 的結(jié)果都會是 object[object Object],所以我們可以使用 JSON.stringify() 將對象序列化,來避免相同的鍵值對。
let arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; let obj={} let result=arr.filter((item)=>{ return obj.hasOwnProperty( //因為JSON.stringify(NaN)==null,所以先檢測是否為數(shù)字類型,數(shù)字類型直接返回,否則再JSON.stringify typeof item=="number"?item:JSON.stringify(item))? false:(obj[typeof item=="number"?item:JSON.stringify(item)]=1); }) // 全都可以去重,完美解決方案! console.log(result) // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {}, {a:1}, {b:2}]
輸出結(jié)果:全都可以去重,無bug,完美解決方案!
我的結(jié)論:Object鍵值對 + hasOwnProperty() + typeOf + JSON.stringify() 判斷依據(jù)是全等===——值和類型都相同,才去重
七、利用filter() + indexOf()去重
用法:array.indexOf(item,start)
start:可選的整數(shù)參數(shù)。規(guī)定在數(shù)組中開始檢索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略該參數(shù),則將從字符串的首字符開始檢索。
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN,NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(arr) { return arr.filter(function (item, index, arr) { //當(dāng)前元素,在原始數(shù)組中的第一個索引==當(dāng)前索引值,否則返回當(dāng)前元素 return arr.indexOf(item, 0) === index; }); } // console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, 'NaN', 0, 'a', {} , {} , {a:1} , {b:2} , {a:1}]
輸出結(jié)果:對象 無法去重,且NaN會被去(忽略)掉,直接消失了,其他都可以去重,包括null。indexOf() 方法無法識別數(shù)組的 NaN 和 {} 成員。
我的結(jié)論:filter() 判斷依據(jù)是全等===——值和類型都相同,才去重
八、利用遞歸去重
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(arr) { var array = arr; var len = array.length; array.sort(function (a, b) { // 排序后更加方便去重 return a - b; }) function loop(index) { if (index >= 1) { if (array[index] === array[index - 1]) { array.splice(index, 1); } loop(index - 1); // 遞歸loop,然后數(shù)組去重 } } loop(len - 1); return array; } // console.log(unique(arr)) // [1, '1', true, 2, 'true', false, null, 0, true, 15, NaN, NaN, 'NaN', 'a', {…}, {…}, {…}, {…}, {…}, undefined]
輸出結(jié)果:NaN 和 對象 無法去重,其他都可以實現(xiàn)去重,包括null。indexOf() 方法無法識別數(shù)組的 NaN 和 {} 成員。
我的結(jié)論:遞歸去重法的判斷依據(jù)是全等===——值和類型都相同,才去重
九、利用Set數(shù)據(jù)結(jié)構(gòu)(ES6)
隨著 ES6 的到來,去重的方法又有了進(jìn)展,比如我們可以使用 Set 和 Map 數(shù)據(jù)結(jié)構(gòu),以 Set 為例,ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set。它類似于數(shù)組,但是成員的值都是唯一的,沒有重復(fù)的值。Set 本身是一個構(gòu)造函數(shù),用來生成 Set 數(shù)據(jù)結(jié)構(gòu)。
因為Set是一個類似數(shù)組結(jié)構(gòu),所以需要用 Array.from() 方法解析類數(shù)組為數(shù)組,將其轉(zhuǎn)型為真正的數(shù)組去使用。Array.from 方法可以將 Set 結(jié)構(gòu)轉(zhuǎn)為數(shù)組。
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique (arr) { return Array.from(new Set(arr)) } // 對象 無法去重,會被保留下來,但可對 NaN、null 去重 console.log(unique(arr)) //[1, 2, '1', "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {} , {a:1} , {b:2} , {a:1}]
甚至可以再簡化下:語法糖——復(fù)制一個數(shù)組: var arr2=[...arr1]
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(arr) { // 利用 [...new Set(arr)] 轉(zhuǎn)為數(shù)組后去重 return [...new Set(arr)]; } console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]
還可以再簡化下:轉(zhuǎn)為ES6 箭頭函數(shù)
var arr = [1, 1, 2, '1', 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}, { a: 1 }, { b: 2 }, { a: 1 }]; var unique = (arr) => [...new Set(arr)] console.log(unique(arr)); // (18) [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]
注:不考慮兼容性,這種使用Set數(shù)據(jù)結(jié)構(gòu)去重的方法代碼最少。而且這種方法還無法去重對象,不過可以對NaN、null進(jìn)行去重,因為 Set 加入值時認(rèn)為NaN等于自身,后面的高階方法會添加去掉重復(fù)“{}”的方法。
輸出結(jié)果:對象 無法去重,其他都可以實現(xiàn)去重,包括NaN、null
我的結(jié)論:newSet() 判斷依據(jù)是全等===——值和類型都相同,才去重
十、利用Map數(shù)據(jù)結(jié)構(gòu)去重(ES6)
了解Map數(shù)據(jù)結(jié)構(gòu)
Map數(shù)據(jù)結(jié)構(gòu)。它類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當(dāng)作鍵。也就是說,Object結(jié)構(gòu)提供了“字符串—值”的對應(yīng),Map 結(jié)構(gòu)提供了“值—值”的對應(yīng),是一種更完善的 Hash 結(jié)構(gòu)實現(xiàn)。
Map 與 Object 的區(qū)別:Object里的屬性是字符串;Map里的key可以是任意的數(shù)據(jù)類型 key:value
對Map來說,我們可以通過使用set,get,has,delete等方法來對Map進(jìn)行操作
創(chuàng)建一個空Map數(shù)據(jù)結(jié)構(gòu),遍歷需要去重的數(shù)組,把數(shù)組的每一個元素作為key存到Map中,由于Map中不會出現(xiàn)相同的key值,所以,最終得到的就是去重后的結(jié)果。
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(arr) { // Map與Object的區(qū)別 Object里的屬性是字符串;Map里的key可以是任意的數(shù)據(jù)類型 key:value let map = new Map(); let array = new Array(); // 數(shù)組用于返回結(jié)果 for (let i = 0; i < arr.length; i++) { // has方法返回一個布爾值,表示某個鍵是否在當(dāng)前 Map 對象之中。 if (map.has(arr[i])) { // 如果有該key值 // set方法設(shè)置鍵名key對應(yīng)的鍵值為value,然后返回整個 Map 結(jié)構(gòu)。如果key已經(jīng)有值,則鍵值會被更新,否則就新生成該鍵。 map.set(arr[i], true); } else { map.set(arr[i], false); // 如果沒有該key值 array.push(arr[i]); } } return array; } // console.log(unique(arr)) // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]
輸出結(jié)果:對象 無法去重,其他都可以實現(xiàn)去重,包括NaN、null
我的結(jié)論:Map數(shù)據(jù)結(jié)構(gòu)去重法 判斷依據(jù)是全等===——值和類型都相同,才去重
十一、利用Map數(shù)據(jù)結(jié)構(gòu) + filter()
var arr = [1, 1, 2, '1', 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}, { a: 1 }, { b: 2 }, { a: 1 }]; function unique(arr) { const seen = new Map() return arr.filter((item) => !seen.has(item) && seen.set(item, 1)) } console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]
輸出結(jié)果:對象 無法去重,其他都可以實現(xiàn)去重,包括NaN、null
我的結(jié)論:Map數(shù)據(jù)結(jié)構(gòu)去重法 判斷依據(jù)是全等===——值和類型都相同,才去重
十二、利用reduce() + includes()去重
Array.reduce 為數(shù)組的歸并方法,使用場景很多,比如求和(累加)、求乘積(累乘),計次,去重,多維轉(zhuǎn)一維,屬性求和等...
var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}]; function unique(arr) { return arr.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []); } // 對象 無法去重 console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]
輸出結(jié)果:對象 無法去重,其他都可以實現(xiàn)去重,包括NaN、null
我的結(jié)論:reduce() + includes()去重法 判斷依據(jù)是全等===——值和類型都相同,才去重
十三、利用findIndex() + Object.is()去重
—— findIndex() ——
findIndex() 方法返回傳入一個測試條件(函數(shù))符合條件的數(shù)組第一個元素位置。
findIndex() 方法為數(shù)組中的每個元素都調(diào)用一次函數(shù)執(zhí)行:
- 當(dāng)數(shù)組中的元素在測試條件時返回 true 時, findIndex() 返回符合條件的元素的索引位置,之后的值不會再調(diào)用執(zhí)行函數(shù)。
- 如果沒有符合條件的元素返回 -1
注意: findIndex() 對于空數(shù)組,函數(shù)是不會執(zhí)行的。
注意: findIndex() 并沒有改變數(shù)組的原始值。
—— Object.is() ——
Object.is() 方法用來判斷兩個值是否相等,它接收兩個參數(shù),分別是需要比較的兩個值。
返回一個 Boolean 值表示這兩個值是否相等。
var arr = [1, 1, 2, '1', 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}, { a: 1 }, { b: 2 }, { a: 1 }]; function unique(arr) { let result = []; for (let i = 0; i < arr.length; i++) { // Object.is()方法認(rèn)為NaN等于自身 if (result.findIndex(value => Object.is(arr[i],value)) === -1) { result.push(arr[i]); } } return result; } // console.log(unique(arr));
輸出結(jié)果:對象 無法去重,其他都可以實現(xiàn)去重,包括NaN、null
我的結(jié)論:reduce() + includes()去重法 判斷依據(jù)是全等===——值和類型都相同,才去重
十四、對象數(shù)組去重
方法一:使用Map數(shù)據(jù)結(jié)構(gòu)
Map
是一組鍵值對的結(jié)構(gòu),用于解決以往不能用對象做為鍵的問題,具有極快的查找速度。(注:函數(shù)、對象、基本類型都可以作為鍵或值。
const ary = [{id: 1, text: "1"}, {id: 1, text: "1"}, {id: 2, text: "2"}, {id: 3, text: "3"}]; const myseta = (ary) => { const map = new Map(); return ary.filter((ary) => !map.has(ary.id) && map.set(ary.id, 1)) }; console.log(myseta(ary)); // [{id: 1, text: '1'}, {id: 2, text: '2'}, {id: 3, text: '3'}]
方法二:使用Set數(shù)據(jù)結(jié)構(gòu)
JSON.stringify + new Set( ) + Array.from() + JSON.parse
使用Set數(shù)據(jù)結(jié)構(gòu)去除重復(fù)對象,必須把對象轉(zhuǎn)為string字符串形式,才可以實現(xiàn)對象去重,所以需要new Set(strings)進(jìn)行轉(zhuǎn)型。
因為Set數(shù)據(jù)結(jié)構(gòu)并非真正的數(shù)組,它類似于數(shù)組,并且成員值都是唯一的,沒有重復(fù),所以可以用來做去重操作。但是因為它是一個類似數(shù)組結(jié)構(gòu),所以需要轉(zhuǎn)型為真正的數(shù)組去使用。所以,需要用Array.from
const ary = [{id: 1, text: "1"}, {id: 1, text: "1"}, {id: 2, text: "2"}, {id: 3, text: "3"}]; const myseta = (ary) => { const strings = ary.map((item) => JSON.stringify(item)) // 使用Set數(shù)據(jù)結(jié)構(gòu)去重對象 // return new Set(strings) // 使用Array.from()把Set數(shù)據(jù)結(jié)構(gòu)去重對象后的結(jié)構(gòu),轉(zhuǎn)為數(shù)組 // return Array.from(new Set(strings)) // 使用Array.from()轉(zhuǎn)為數(shù)組,然后再使用數(shù)組的map方法把數(shù)組里面的字符串類型轉(zhuǎn)化為對象類型: return Array.from(new Set(strings)).map((item) => JSON.parse(item)) }; // console.log(myseta(ary)); // Set(3) {'{"id":1,"text":"1"}', '{"id":2,"text":"2"}', '{"id":3,"text":"3"}'} // console.log(myseta(ary)); // ['{"id":1,"text":"1"}', '{"id":2,"text":"2"}', '{"id":3,"text":"3"}'] console.log(myseta(ary)); // [{"id":1,"text":"1"}', '{"id":2,"text":"2"}', '{"id":3,"text":"3"}]
定義和用法
map() 方法返回一個新數(shù)組,數(shù)組中的元素為原始數(shù)組元素調(diào)用函數(shù)處理后的值。
map() 方法按照原始數(shù)組元素順序依次處理元素。
注意: map() 不會對空數(shù)組進(jìn)行檢測。
注意: map() 不會改變原始數(shù)組。
方法總結(jié)
如果有如下這樣一個數(shù)組 :
var arr = [ 0, 0, 1, 1, '1', '1', 'true', 'true', true, true, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 'a', 'a', new String('1'), new String('1'), /a/, /a/, {}, {}, { a: 1 }, { b: 2 }, { a: 1 }];
由于各去重方法的兼容性不同,所得到的去重結(jié)果就會有所有不同,下面來查看一下各方法的兼容性差異:
方法 | 結(jié)果 | 說明 |
---|---|---|
for循環(huán) | [0, 1, '1', 'true', true, false, undefined, null, NaN, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | NaN 和 對象、正則、構(gòu)造函數(shù)不去重; |
splice() + for循環(huán) | [0, 1, 'true', undefined, NaN, NaN, 'NaN', 'a', /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | NaN 和 對象、正則不去重;null、構(gòu)造函數(shù)被忽略掉;字符串?dāng)?shù)字、boolean的true、false轉(zhuǎn)為數(shù)字 |
indexOf() + for循環(huán) | [0, 1, '1', 'true', true, false, undefined, null, NaN, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | NaN 和 對象、正則、構(gòu)造函數(shù)不去重; |
sort() + for循環(huán) | [/a/, /a/, 0, 1, String, NaN, NaN, 'NaN', {…}, {…}, {…}, {…}, {…}, 'a', false, null, 'true', true, undefined]? | NaN 和 對象、正則不去重;new String('1') 和 ‘1’等價,所以會去重 |
includes() | [0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | 對象、正則、構(gòu)造函數(shù)不去重 |
優(yōu)化后的鍵值對方法 | [0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', /a/, {…}, {…}]? | 全去重,完美解決方案! |
filter()+indexOf() | [0, 1, '1', 'true', true, false, undefined, null, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | 對象、正則、構(gòu)造函數(shù)不去重;NaN會被忽略掉 |
遞歸 | [0, false, null, 1, '1', 'true', true, NaN, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}, undefined]? | NaN 和 對象、正則、構(gòu)造函數(shù)不去重; |
Set數(shù)據(jù)結(jié)構(gòu) | [0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | 對象、正則、構(gòu)造函數(shù)不去重; |
Map數(shù)據(jù)結(jié)構(gòu) | [0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | 對象、正則、構(gòu)造函數(shù)不去重; |
Map數(shù)據(jù)結(jié)構(gòu) + filter() | [0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | 對象、正則、構(gòu)造函數(shù)不去重; |
reduce() + includes() | [0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | 對象、正則、構(gòu)造函數(shù)不去重; |
findIndex() + Object.is() | [0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]? | 對象、正則、構(gòu)造函數(shù)不去重; |
注意:上述表格的說明部分,除了不去重的、被去重跳過忽略掉的之外,其他的都可以實現(xiàn)去重,包括null、NaN(當(dāng)然,僅限在去重數(shù)組范圍內(nèi)的項,不在測試去重數(shù)組范圍內(nèi)其他類型數(shù)據(jù),大家可自行測試。),以上方法的整理總結(jié),如有錯誤之處,還請大家評論指出!
寫在最后
雖然各個方法去重的結(jié)果有所不同,但更重要的是讓我們知道在合適的場景要選擇合適的去重方法。
另外,還有很多文章提到其他去重的方法,比如,foreach + indexOf 數(shù)組去重的方法,不過,實現(xiàn)的過程,個人覺得都是大同小異。如果還有其他比較好的去重方法,歡迎評論給出!
以上就是JavaScript實現(xiàn)數(shù)組去重的14種方法大全的詳細(xì)內(nèi)容,更多關(guān)于JavaScript數(shù)組去重的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript尾遞歸的實現(xiàn)及應(yīng)用場景
本文主要介紹了JavaScript尾遞歸的實現(xiàn)及應(yīng)用場景,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05javascript ready和load事件的區(qū)別示例介紹
ready是在DOM加載完成就觸發(fā);load是在加載完所有頁面內(nèi)容才會觸發(fā),下為大家簡要介紹下,不知道的朋友可以參考下2013-08-08Echarts圖表移動端橫屏進(jìn)入退出的實現(xiàn)
本文主要介紹了Echarts圖表移動端橫屏進(jìn)入退出的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05詳述JavaScript實現(xiàn)繼承的幾種方式(推薦)
這篇文章主要介紹了詳述JavaScript實現(xiàn)繼承的幾種方式(推薦)的相關(guān)資料,需要的朋友可以參考下2016-03-03完美實現(xiàn)js拖拽效果 return false用法詳解
這篇文章主要為大家詳細(xì)介紹了完美實現(xiàn)js拖拽效果的代碼,一起學(xué)習(xí)return false的用法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07微信小程序web-view不支持打開非業(yè)務(wù)域名https?//XXXX?請重新配置的解決辦法
小程序現(xiàn)在日漸成熟,功能也越來越強(qiáng)大,我們今天來一起看看小程序跳轉(zhuǎn)H5頁面的問題,下面這篇文章主要給大家介紹了關(guān)于微信小程序web-view不支持打開非業(yè)務(wù)域名https?//XXXX?請重新配置的解決辦法,需要的朋友可以參考下2022-08-08