数组去重,做为前端面试中的经典题,常常会被说起到。前端
本文就数组去重的方法,作了些许整理,方便理解与总结。面试
在不借助JS数组的相关API的状况下,咱们来看下数组去重最原始的实现方法:数组
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']
复制代码
在这个方法中,咱们使用循环嵌套,最外层循环 array,里面循环 res,若是 array[i] 的值跟 res[j] 的值相等,就跳出循环,若是都不等于,说明元素是惟一的,这时候 j 的值就会等于 res 的长度,根据这个特色进行判断,将值添加进 res。数据结构
这个最原始的方法,有着一个很明显的优势,兼容性好。ui
借助indexOf,咱们能够稍稍简化下内层循环:spa
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']
复制代码
ES5提供了filter方法,咱们能够借助这个方法来简化外层循环,好比改写使用indexOf的去重方法:code
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对象,咱们把数组的值存成object对象的key值,好比让object[array[item1]] = true
,在判断另外一个值object[array[item2]]
存在的话,就说明该值是重复的:对象
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]
复制代码
很明显,如今这个方法是有问题,由于1和'1'是不一样的,可是object[1]
和object['1']
倒是同一个引用,由于对象的key值只能是字符串。字符串
那么咱们使用 typeof item + item
拼成字符串做为key值来避免这个问题:string
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']
复制代码
But,若是数组项中若是存在对象时,好比{value:1}、{value:2},因为typeof item + item
的结果都会是object[object Object]
,不过咱们可使用JSON.stringfy()
将对象序列化来避免相同的键值。因此可这样改写unique:
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}]
复制代码
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中的Map对象来改写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 会被忽略掉 |
对象键值对去重 | [1, "1", null, undefined, String, /a/, NaN] | 所有去重 |
Set对象去重 | [1, "1", null, undefined, String, String, /a/, /a/, NaN] | 对象不去重 NaN 去重 |
至此,经常使用的数组去重方法都已展现在这里,对于不一样场景的使用也作了对比,方便理解与应用。