这种函数的优点是兼容性好比较通用,缺点是时空复杂度都很直观的为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 简化内层代码,优化上一 两层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 简化外层循环,优化上一 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‘]
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类型,所以我们可以通过 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类型),所以除了数组中的第一个对象,所有的对象都会被过滤。
上一方法说到 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。
根据MDN描述:
Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。(赋值、浅拷贝、深拷贝的方法会在后面的文章详解) |
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"]
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 会被忽略掉 |
对象键值对去重 | [1, "1", null, undefined, String, /a/, NaN] | 全部去重 |
Set对象去重 | [1, "1", null, undefined, String, String, /a/, /a/, NaN] | 对象不去重 NaN 去重 |
相关资源:JS数组去重方法总结.
原文:https://www.cnblogs.com/97z4moon/p/14721810.html