摘要:工作過程中經常會用到數組去重,用到的時候往往一時想不到好方法,所以這里來總結一下去重方法。和方法分別為添加成員方法和得到鍵值方法。因此,利用方法也可以實現數組的去重。
工作過程中經常會用到數組去重,用到的時候往往一時想不到好方法,所以這里來總結一下去重方法。
使用es6去重代碼很簡單,而且ES6已經相當普及了。所以先來介紹一下es6中的方法。
function unique (arr) { const seen = new Map() return arr.filter((a) => !seen.has(a) && seen.set(a, 1)) } let arr = [1, 2, 1, 3, "1", NaN, NaN, null, null, undefined, undefined, {}, {}]; console.log(uniMap(arr)); //[ 1, 2, 3, "1", NaN, null, undefined, {}, {} ]
Map是es6中新增的數據結構,它類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。也就是說,Object 結構提供了“字符串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實現。
Map的has方法用于判斷map是否含有該鍵。set 和get 方法分別為添加成員方法和得到鍵值方法。
上述方法一方面利用了map的has和set方法,一方面利用了數組的 filter方法,返回結果為真的元素組成的數組。
注意
Map 的鍵實際上是跟內存地址綁定的,只要內存地址不一樣,就視為兩個鍵。這句話不好理解的話,可以這樣說如果 Map 的鍵是一個簡單類型的值(數字、字符串、布爾值),則只要兩個值嚴格相等,Map 將其視為一個鍵,比如0和-0就是一個鍵,布爾值true和字符串true則是兩個不同的鍵, 對象是不同的鍵;另外,undefined和null也是兩個不同的鍵。雖然NaN不嚴格相等于自身,但 Map 將其視為同一個鍵
function uniMap (arr) { return [...new Set(arr)]; } let arr = [1, 2, 1, 3, "1", NaN, NaN, null, null, undefined, undefined, {}, {}]; console.log(uniMap(arr)); // [ 1, 2, 3, "1", NaN, null, undefined, {}, {} ]
Set 也是ES6 提供的新的數據結構。它類似于數組,但是成員的值都是唯一的,沒有重復的值。
Set 本身是一個構造函數,用來生成Set數據結構,它也接受一個數組或具有iterator接口的數據結構作為參數初始化。上述代碼中就是利用了這個特性來實現對數組的去重。
Set 具有add方法來添加某個值,返回set結構本身。因此,利用add方法也可以實現數組的去重。例如:
const s = new Set(); function uniMap(arr) { arr.forEach( item => s.add(item)); return [...s]; // [ 1, 2, 3, "1" ] } // main let arr = [1, 2, 1, 3, "1", 2, 2, 2]; console.log(uniMap(arr));
Array.from也是ES6中的新方法可以將 Set 結構轉為數組。這就引出第三種使用set的數組去重方法:
// set3 function uniMap(arr) { return Array.from(new Set(arr)); } let arr = [1, 2, 1, 3, "1", NaN, NaN, null, null, undefined, undefined, {}, {}]; console.log(uniMap(arr)); // [ 1, 2, 3, "1", NaN, null, undefined, {}, {} ]
注意
Set 內部判斷兩個值是否不同,使用的算法叫做“Same-value-zero equality”,它類似于精確相等運算符(===),所以上述結果中 1 和 "1" 認為是不相同的,都被保留下來,主要的區別是NaN等于自身,而精確相等運算符認為NaN不等于自身。另外,兩個對象總是不相等的。
function uniMap(arr) { let res = []; for(let i = 0, arrLen = arr.length; i < arrLen; i += 1) { let j = 0, resLen = res.length; for(; j < resLen; j +=1) { if(arr[i] === res[j]) { break; } } if( j === resLen) { res.push(arr[i]); } } return res; } // main let arr = [1, 2, 1, 3, "1", 2, 2, 2]; console.log(uniMap(arr)); // [ 1, 2, 3, "1" ] arr = [1, 2, 1, 3, "1", NaN, NaN, null, null, undefined, undefined, {}, {}]; console.log(uniMap(arr)); //[ 1, 2, 3, "1", NaN, NaN, null, undefined, {}, {} ]
我們使用循環嵌套,最外層循環 array,里面循環 res,如果 array[i] 的值跟 res[j] 的值相等,就跳出循環,如果都不等于,說明元素是唯一的,這時候 j 的值就會等于 res 的長度,根據這個特點進行判斷,將值添加進 res。
這個是最基本的方法,但是第一次寫還真犯了錯,無法去重。代碼是這樣的:
function uniMap(arr) { let res = []; for(let i = 0, arrLen = arr.length; i < arrLen; i += 1) { let j = 0, resLen = res.length; for(; j < resLen; j +=1) { if(arr[i] === res[j]) { break; } res.push(arr[i]); } } return res; // [] } // main let arr = [1, 2, 1, 3, "1", 2, 2, 2]; console.log(uniMap(arr));
和上面代碼相比,就是res.push(arr[i]);放在了內循環里,少了 j === resLen的判斷,就得到了空數組。原因是 初始的時候res.length = 0,不會進到內循環,所以res始終為空。果然眼高手低啊~
4. indexOf方法優化雙重循環中的內部循環// 雙重循環2 function uniMap(arr) { let res = []; for(let i = 0, arrLen = arr.length; i < arrLen; i += 1) { if( res.indexOf(arr[i]) === -1) { res.push(arr[i]); } } return res; } // main let arr = [1, 2, 1, 3, "1", 2, 2, 2]; console.log(uniMap(arr)); // [ 1, 2, 3, "1" ] arr = [1, 2, 1, 3, "1", NaN, NaN, null, null, undefined, undefined, {}, {}]; console.log(uniMap(arr)); // [ 1, 2, 3, "1", NaN, NaN, null, undefined, {}, {} ]5. filter方法優化雙重循環中的外層循環
// filter方法 function uniMap(arr) { let res = []; return res = arr.filter( (item , index) => { return arr.indexOf(item) === index; }) //[ 1, 2, 3, "1", null, undefined ] } // main let arr = [1, 2, 1, 3, "1", NaN, NaN, null, null, undefined, undefined, {}, {}]; console.log(uniMap(arr)); // [ 1, 2, 3, "1", null, undefined, {}, {} ]
此處的filter方法可以和方法3,4排列組合用~~其實方法1也利用了filter方法(filter人氣高啊)filter方法原理已經說過,忘記的往上翻~
6. Object 方法// object function uniMap(arr) { let obj = {}; return arr.filter( item => { return obj.hasOwnProperty(item) ? false : (obj[item] = true); }) } // main let arr = [1, 2, 1, 3, "1", 2, 2, 2, NaN, NaN, null, null, undefined, undefined]; console.log(uniMap(arr)); // [ 1, 2, 3, NaN, null, undefined ]
上述代碼原理是利用一個空的 Object 對象,我們把數組的值存成 Object 的 key 值,比如 Object[value1] = true,在判斷另一個值的時候,如果 Object[value2]存在的話,就說明該值是重復的。從結果可以看到,他把數字 1 和字符串 "1"當成了同一個字符,因為對象的key值均是字符串,數字1被轉換為字符串了,因此該方法適用于你想把數字和字符串去重的場合。
特殊數據結構的去重判斷去重的方法就到此結束了,然而根據上面的結果可以看到,對于特殊的數據類型比如:null、undefined、NaN、對象等,不同的去重方法其實結果是不同的。那么下面給個總結和分析。
對于例子中的這樣一個數組:
[1, 2, 1, 3, "1", 2, 2, 2, NaN, NaN, null, null, undefined, undefined];
方法 | 結果 | 說明 |
---|---|---|
1.Map | [ 1, 2, 3, "1", NaN, null, undefined, {}, {} ] | 對象不去重 |
2.Set | [ 1, 2, 3, "1", NaN, null, undefined, {}, {} ] | 對象不去重 |
3.雙重循環 | [ 1, 2, 3, "1", NaN, NaN, null, undefined, {}, {} ] | 對象和NaN都不去重 |
4.內層index | [ 1, 2, 3, "1", NaN, NaN, null, undefined, {}, {} ] | 對象和NaN不去重 |
5.外層filter | [ 1, 2, 3, "1", null, undefined, {}, {} ] | 對象不去重NaN被忽略掉 |
6.Object方法 | [ 1, 2, 3, NaN, null, undefined ] | 數字和字符串去重,對象被忽略 |
之所以出現上面的結果,先看一下幾個判斷:
console.log(null === null); // true console.log(undefined === undefined); // true console.log(NaN === NaN); // false console.log({} === {}); // false
再結合 indexOf 是使用 === 判斷,以及set map 也使用 === 判斷但是認為 NaN 和 NaN 相等,便可以分析出來。
注意
對于數組元素和去重不是上述類型和結果的,那么針對你想要的去重去靈活修改代碼,不可以生搬硬套~~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93352.html
摘要:而數組元素去重是基于運算符的。而如果有迭代函數,則計算傳入迭代函數后的值,對值去重,調用方法,而該方法的核心就是調用方法,和我們上面說的方法一異曲同工。 Why underscore (覺得這部分眼熟的可以直接跳到下一段了...) 最近開始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計劃中。 閱讀一些著名框架類庫的源碼,就好像...
摘要:專題系列第三篇,講解各種數組去重方法,并且跟著寫一個前言數組去重方法老生常談,既然是常談,我也來談談。它類似于數組,但是成員的值都是唯一的,沒有重復的值。 JavaScript 專題系列第三篇,講解各種數組去重方法,并且跟著 underscore 寫一個 unique API 前言 數組去重方法老生常談,既然是常談,我也來談談。 雙層循環 也許我們首先想到的是使用 indexOf 來循...
摘要:專題系列共計篇,主要研究日常開發中一些功能點的實現,比如防抖節流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點是研究專題之函數組合專題系列第十六篇,講解函數組合,并且使用柯里化和函數組合實現模式需求我們需要寫一個函數,輸入,返回。 JavaScript 專題之從零實現 jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實現一個 jQuery 的 ext...
摘要:寫在前面專題系列是我寫的第二個系列,第一個系列是深入系列。專題系列自月日發布第一篇文章,到月日發布最后一篇,感謝各位朋友的收藏點贊,鼓勵指正。 寫在前面 JavaScript 專題系列是我寫的第二個系列,第一個系列是 JavaScript 深入系列。 JavaScript 專題系列共計 20 篇,主要研究日常開發中一些功能點的實現,比如防抖、節流、去重、類型判斷、拷貝、最值、扁平、柯里...
閱讀 3256·2023-04-26 02:10
閱讀 2880·2021-10-12 10:12
閱讀 4557·2021-09-27 13:35
閱讀 1519·2019-08-30 15:55
閱讀 1058·2019-08-29 18:37
閱讀 3422·2019-08-28 17:51
閱讀 1953·2019-08-26 13:30
閱讀 1190·2019-08-26 12:09