JS数组去重方法总结

JS数组去重

数组去重,作为前端面试中的经典题,经常会被提及到。

本文就数组去重的方法,做了些许整理,方便理解与总结。

最原始的方法

在不借助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。

这个最原始的方法,有着一个很明显的优点,兼容性好。

indexOf

借助indexOf,我们可以稍稍简化下内层循环:

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

ES5提供了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对象,我们把数组的值存成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值来避免这个问题:

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}]
复制代码

ES6的Set对象和Map对象

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 去重

至此,常用的数组去重方法都已展示在这里,对于不同场景的使用也做了对比,方便理解与应用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值