JS009. 数组去重的多种方法总结与一步步优化
两层for循环
这种函数的优点是兼容性好比较通用,缺点是时空复杂度都很直观的为O(n2),不利于维护和性能。
var array = [1,1,'1','1']
function unique(array) {
var res = []
for (var i=0,arrlen=array.length;i<arrlen;i++) {
for (var j = 0,reslen=res.length;j<reslen;j++) {
if (array[i] === res[j]) {
break
}
}
if (j === reslen) {
res.push(array[i])
}
}
return res
}
console.log(unique(array)) //[1,'1']
indexOf()
通过 indexOf 简化内层代码,优化上一 两层for循环 方法。
var array = [1,1,'1','1']
function unique(array) {
var res = []
for (var i=0,len=array.length;i<len;i++) {
var current = array[i]
if (res.indexOf(current) === -1) {
res.push(current)
}
}
return res
}
console.log(unique(array)) // [1,'1']
filter()
通过 filter 简化外层循环,优化上一 indexof 方法。
var array = [1,2,1,1,'1']
function unique(array) {
var res = array.filter(function(item,index,array) {
return array.indexOf(item) === index
})
return res
}
console.log(unique(array)) // [1,2,'1']
Object键值对
- 通过 filter 为数组的每一个元素做条件过滤。
- 再通过 三元运算 将数组的元素作为key登录在obj中,将它对应的值设为true。
- 通过 hasOwnProperty 判断对象是否包含某一属性(键),如果包含即返回false, filter 接收到false将会过滤掉该 item (array[index]) 的return事件。
var array1 = [1,2,1,2,1]
var array2 = [1, 2, 1, 1, '1'];
function unique(array) {
var obj = {}
return array.filter(function(item, index, array){
return obj.hasOwnProperty(item) ? false : (obj[item] = true)
})
}
console.log(unique(array1)) //[1,2]
console.log(unique(array2)) // [1, 2]
- 缺点:由于Object的键只能是String类型,因此 obj[1] 与 obj['1'] 是等价的,它们引用同一个堆栈,最终在第二数组实参中 1 和 '1' 被去重为同一个元素。
typeof()
上一个方法说到Object的键只能是String类型,所以我们可以通过 typeof item + item 的方式拼成一个字符串,来作为 hasOwnProperty 的判断依据:
var array = [1,2,1,1,'1']
function unique(array) {
var obj = {}
return array.filter(function(item,index,array) {
return obj.hasOwnProperty(typeof item+item)?false:(obj[typeof item+item] = true)
})
}
console.log(unique(array)) //[1,2,'1']
array[0]和array[4]分别被 typeof 拼接成 "number1" 和 "string1",很好的区分了不同类型转字符串后的区别。
但是如果数组中存在对象,比如[ { name: 97 }, { descript: 'z' } ],由于 typeof item + item 的结果都会是 object[object Object] (String类型),所以除了数组中的第一个对象,所有的对象都会被过滤。
JSON.stringify() - 终极解决方案
上一方法说到 typeof item + item 两个拥有不同键值对的对象都会返回object[object Object],所以我们可以使用 JSON.stringify() 将对象序列化来避免相同的键值对。
var array = [1,1,2,2,'1',{value: 1}, {value: 1}, {value: 2}]
function unique(array) {
var obj = {}
return array.filter(function(item, index, array){
console.log(typeof item + JSON.stringify(item))
return obj.hasOwnProperty(typeof item + JSON.stringify(item)) ? false : (obj[typeof item + JSON.stringify(item)] = true)
})
}
console.log(unique(array)) // [1,2,'1',{value: 1}, {value: 2}]
obj[typeof item + JSON.stringify(item)] 中假设item是object类型,就会被格式化成JSON字符串,即可区分两个不同的object。
Array.from()
根据MDN描述:
| Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。(赋值、浅拷贝、深拷贝的方法会在后面的文章详解) |
ES6 - Set()
Set 对象是ES6中的新数据结构,根据MDN描述:
| Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。 |
也就是说,Set对象类似于数组,但是成员的值都是唯一的,没有重复的值。那么借助于Set,数组去重将变得异常简单:
var array = [1, 2, 1, '1']
function unique(array) {
return Array.from(new Set(array))
}
console.log(unique(array)) // [1, 2, "1"]
ES6 - Array.prototype.Map()
Map() 在参数上和 filter() 类似,可以通过它来改写 unique().
var array = [1, 2, 1, '1'];
function unique(array) {
const seen = new Map()
return array.filter(function(item,index,array) {
return !seen.has(item) && seen.set(item,1)
})
}
console.log(unique(array)) //[1,2,'1']
总结
如果有这样一个数组
var array = [1, 1, '1', '1', null, null, undefined, undefined, new String('1'), new String('1'), /a/, /a/, NaN, NaN];
用不同的方法对其去重,来查看方法的兼容性:
| 方法 | 结果 | 说明 |
|---|---|---|
| for循环 | [1, "1", null, undefined, String, String, /a/, /a/, NaN, NaN] | 对象和 NaN 不去重 |
| indexOf | [1, "1", null, undefined, String, String, /a/, /a/, NaN, NaN] | 对象和 NaN 不去重 |
| filter+indexOf | [1, "1", null, undefined, String, String, /a/, /a/] | 对象不去重 NaN 会被忽略掉 |
| 对象键值对去重(JSON.stringify()) | [1, "1", null, undefined, String, /a/, NaN] | 全部去重 |
| Set对象去重 | [1, "1", null, undefined, String, String, /a/, /a/, NaN] | 对象不去重 NaN 去重 |
相关资源:JS数组去重方法总结.
- END -
JS009. 数组去重的多种方法总结与一步步优化的更多相关文章
- js数组去重(多种方法)
// js数组去重 Array.prototype.fun1 = function(){ var arr = this, result = [], i, len = arr.length; for(i ...
- js数组去重五种方法
今天来聊一聊JS数组去重的一些方法,包括一些网上看到的和自己总结的,总共5种方法(ES5). 第一种:遍历数组法 这种方法最简单最直观,也最容易理解,代码如下: var arr = [2, 8, 5, ...
- js数组去重的hash方法
对于 JavaScript 数组去除重复项,现在有多种方法,其中一种是hash,如下: if (!Array.prototype.unique) { Array.prototype.unique = ...
- JS数组去重的十种方法
一.前言: 我们在实际工作中,或者在面试找工作时,都会用到或者被问到一个问题,那就是"数组如何去重".是的,这个问题有很多种解决方案,看看下面的十种方式吧! 二.数组去重方式大汇总 ...
- 数组-去重、排序方法、json排序
1.数组去重 /*方法一: 1,'1' 会被认为是相同的; 所有hash对象,如:{x;1},{y:1}会被认为是相同的 //10ms */ Array.prototype.unique=functi ...
- JS中数组去重的九方法
数组去重方法 方法一:运用set结构特点:存储的数据没有重复的,结果为对象,再用Array.from()转换成数组 var arr = [1,1,2,1,3,4,5] ...
- php数组去重、魔术方法、redis常用数据结构及应用场景
一.用函数对数组进行去重的方法 1.arrau_unique函数的作用 移除数组中重复的值. 将值作为字符串进行排序,然后保留每个值第一次出现的健名,健名保留不变. 第二个参数可以选择排序方式: SO ...
- (PASS)JAVA数组去重 三种方法 (不用集合)
第一种方法(只学到数组的看): 定义一个新的数组长度和旧数组的长度一样,存储除去重复数据的旧数组的数据和0, package demo01; import java.sql.Array; import ...
- JavaScript数组去重(5种方法)
// 数组去重 let arr = ['a', 'b', 'b', 1, 1, 'true', true, true, NaN, NaN, 'NaN', undefined, undefined, n ...
随机推荐
- 数据结构与算法 java描述 第一章 算法及其复杂度
目录 数据结构与算法 java描述 笔记 第一章 算法及其复杂度 算法的定义 算法性能的分析与评价 问题规模.运行时间及时间复杂度 渐进复杂度 大 O 记号 大Ω记号 Θ记号 空间复杂度 算法复杂度及 ...
- Quartz 实现同一辅助类 重复开启多任务
前言: 最近做一个项目,需要用到定时任务,第一就想到了Quartz,然后很开心的就实现了功能,但是后来发现一个问题,如果需要开启多个定时任务,需要写多个辅助类,而辅助类里面的功能基本差不多,这是我就想 ...
- gRPC学习之四:实战四类服务方法
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 绿色djvu阅读软件
官方的djvu viewer都需要安装,总算找到一个绿色版的,名为STDU Viewer,可以阅读的格式包括DjVu, PDF, TIFF, XPS, FB2等,版本为1.6.2.
- CSS 奇思妙想 | 使用 resize 实现强大的图片拖拽切换预览功能
本文将介绍一个非常有意思的功能,使用纯 CSS 利用 resize 实现强大的图片切换预览功能.类似于这样: 思路 首先,要实现这样一个效果如果不要求可以拖拽,其实有非常多的办法. 将两张图片叠加在一 ...
- PHP下对Mysql数据库的操作
PHP连接数据库: 使用 mysqli-connect()函数,函数里面至少填三个变量:host,用户名,密码. $dbHost="localhost"; $dbUser=&quo ...
- C# 计时器用法(DispatcherTimer、System.Timers.Timer、System.Threading.Timer)
首先,我觉得三种计时器最大的区别是:DispatcherTimer触发的内容会直接转到主线程去执行(耗时操作会卡住主线程),另外两个则是在副线程执行,如果需要修改界面,则需要手动转到主线程. Disp ...
- Mooc中国大学Python学习笔记--数字类型及操作
整数类型 只需知道整数无限制,pow(),4进制表示形式 与数学中整数的概念一致 --可正可负,没有取值范限制 --pow(x,y)函数:计算x^y,想算多大算多大 -十进制:10 -二进制,以0b或 ...
- 微信小程序学习笔记三 路由的基本使用
小程序中路由的使用 1.1 页面路由 在小程序中, 所有页面的路由全部由框架进行管理 1.2 页面栈 框架以栈的形式维护了当前的所有页面, 当发生路由切换的时候, 页面栈的表现如下: 1.3 获取当前 ...
- SpringBoot 优雅配置跨域多种方式及Spring Security跨域访问配置的坑
前言 最近在做项目的时候,基于前后端分离的权限管理系统,后台使用 Spring Security 作为权限控制管理, 然后在前端接口访问时候涉及到跨域,但我怎么配置跨域也没有生效,这里有一个坑,在使用 ...