关于数组去重,也算是一个比较常见的面试题了。但是有点开发经验的同学又会发现,前端数组去重的操作很少见(也可能是我个人经验不足遇到的比较少)。这是为什么呢?。我感觉可能是大部分的去重操作被后端处理了。个人见解!
虽然,前端很少用到。本着爱(kuai)与(su)和(zhang)平(xin)的精神,我们也来学习一波!
IndexOf是数组的一个原生方法,当你传入一个参数的时候,它会返回入参数的索引值。如果未找到就返回 -1。利用这一特性,
function unique(array) {
var res = []
for(var i = 0; i < array.length; i++) {
if(res.indexOf(array[i]) === -1) {
res.push(array[i])
}
}
return res
}
相信一般求职者都能回答出这个,当初我在面试的时候,也回答了这一项。也只是回答出了这一项。后来面试官继续问:还有其他方式吗?这种方式的效率高吗?复杂度是多少?
后来这场面试就失败了,没关系!痛定思痛继续学习。
后来我知道了,indexOf需要把每个参数重新判断一遍。效率很低下。而且indexOf还不能查到NaN
的索引值,如下代码:
var arr = [1, NaN]
arr.indexOf(NaN) // -1
那如何解决这个查询问题呢?我们下次谈include和indexOf的时候细说!
我们先来验证一下indexOf的效率问题?后面想想怎么提高?
我们先准备两个数组,一个长度为:100000 一个:50000
var arr1 = Array.from(new Array(100000), (x, index)=>{
return index
})
var arr2 = Array.from(new Array(50000), (x, index)=>{
return index+index
})
var handleArray = arr1.concat(arr2)
console.log('原数组的长度',handleArray.length)
let start = new Date().getTime()
console.log('开始数组去重...')
function unique(array) {
var res = []
for(var i = 0; i < array.length; i++) {
if(res.indexOf(array[i]) === -1) {
res.push(array[i])
}
}
return res
}
console.log('去重后的长度', unique(handleArray).length)
let end = new Date().getTime()
console.log('耗时', end - start)
执行结果如下:
一个150000的数组,去重要7723毫秒。(该值存在波动,这是一个平均值,后续相同)
我们可以先对数组进行排序,将相似的值排到一起
function unique(array) {
var sortArr = array.concat().sort() //返回新数组
var res = [sortArr[0]]
for(var i=1; i < sortArr.length; i++) {
if(sortArr[i] !== sortArr[i-1]) {
res.push(sortArr[i])
}
}
return res
}
代码解析:
这样的做法,省去了对重复参数的索引。效率上面确实也提升不少。对于一些重复性比较高的数组进行去重,效率明显提升。
我们看一下优化后的效果:
看到这个结果,吓我一跳啊!竟然只要106毫秒。优化真的是一门学问啊!
上面的两种方法都是使用了indexOf这个api,除了这个有没有其他的呢?答案是肯定的。就是利用双层循环
function unique(array){
var res = []
for(var i=0;i<array.length;i++) {
for(var j=0;j<res.length;j++) {
if(array[i] === res[j]) {
break
}
}
if(j === res.length) {
res.push(array[i])
}
}
return res
}
代码解析:
这样的方法,不失为一种新解法。但是由于双层循环的存在,其效率也不会改善多少。
我们看到相比方法一确实快了不少,但是效率还是比较低下的。不推荐大家使用!
这个是es6的语法糖,极度简单明了!效率也是杠杠滴!
function unique(array){
return Array.from(new Set(array)
// return [...new Set(array)]
}
这样的方法,没啥好说的。就是一个字快!
我们看看执行效果:
官方的优化总是不让人失望,真的太牛了!爸爸始终是爸爸!
function unique(array){
var obj = {}
var res = []
for(var i =0; i < array.length; i++) {
if(!obj[array[i]]) {
res.push(array[i])
obj[array[i]] = 1
} else {
obj[array[i]]++
}
}
return res
}
代码解析:
再来看一下:
看到这个图的时候!我不禁多执行了几次。但是事实告诉我:你没有看错,比new Set()还要高效。
重点说明一下:就是对象 1 和 ‘1’ 这样的方式无法区分。需要引入typeof hasOwnProperty等操作。这里就不展开讨论了。
在写去重之前,我也看了网上不少博文。说多少种的都有。其实感觉就是使用了不同的语法糖进行了循环操作和删除操作。比如 filter reduce for of include splice
Map等方法。思路和想法确实很新颖。我把链接放到了下面。还有我自己的测试代码,如需自取。
正在读此文的朋友,如果明天面试官问你这样的问题,你是不是跟我一样的心态呢?
源码地址:
源码
我的小站: https://shenzhiyong.com.cn
参考链接:
JavaScript数组去重(12种方法,史上最全)