摘要:工作过程中经常会用到数组去重,用到的时候往往一时想不到好方法,所以这里来总结一下去重方法。和方法分别为添加成员方法和得到键值方法。因此,利用方法也可以实现数组的去重。
工作过程中经常会用到数组去重,用到的时候往往一时想不到好方法,所以这里来总结一下去重方法。
使用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 相等,便可以分析出来。
注意
对于数组元素和去重不是上述类型和结果的,那么针对你想要的去重去灵活修改代码,不可以生搬硬套~~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/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 篇,主要研究日常开发中一些功能点的实现,比如防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里...
阅读 3237·2023-04-26 02:10
阅读 2797·2021-10-12 10:12
阅读 4414·2021-09-27 13:35
阅读 1441·2019-08-30 15:55
阅读 986·2019-08-29 18:37
阅读 3334·2019-08-28 17:51
阅读 1904·2019-08-26 13:30
阅读 1123·2019-08-26 12:09