Javascript数组查重的探究

相信很多人或多或少都会遇到过数组查重的问题。最近,我也遇到这样一个问题,当我往一个数组里添加元素时,我希望判断这个元素在这个数组中是不是已经存在,存在就不添加,不存在就添加。这个问题本质上就是一个数组查重的问题。

当然了,在js的ES6规范中,已经就数组查重提供了一个Set类,我们可以直接通过这个类,把数组里的重复元素去掉,代码如下:

输出结果为[1,2,3]。

但是,这个方法在实际应用中局限性还蛮大的,首先是它只支持基本类型的查重,假如数组里的元素是对象,那么它判断重复的原则就是引用值相同,因此,哪怕两个对象的字段的值是一样的,由于它们的引用不同,该方法还是无法进行去重。其次就是它返回的数组newArr是一个新的数组,也就是说,我每次添加一个元素,得到的数组都是一个新的数组,这就不能做到在原数组上进行操作了。

看来封装度高虽说在某些时候简化了代码,但是却难以满足某些奇怪的需求,因此,还是需要自己实现一下查重去重功能的。

首先是最简单的,估计也是大家最常用的方法,双循环。这个方法思路很简单,就是在循环里面加一个查重的循环。代码如下:

不要觉得我首先拿出来说,或者一看到是双循环就觉得这个方法不好。其实,在数据种类较少的时候,由于内循环的命中率很高,所以效率还是很不错的。但是这个方法受数据的影响很大。双循环方法对数据种类数的敏感程度是很高的,随着数据种类数的增多,计算时间呈线性上升。比如有一万个待处理数据,所有数据都是相同的,那么这个方法的效率就会很高。但是如果所有数据都不相同,那么效率大概就只有所有数据相同情况下的一万分之一。

第二个方法是建哈希表,javascript有一个很神奇的特性,就是对象的字段可以动态增加和删除,而不需要定义,然后对象可以用字段名访问到该字段的值。

例如有这样一个对象

输出结果为“Ben”。

那么查重的思路来了,首先是建一个空的对象作为哈希表,然后,循环获得待处理数组的元素,把这个元素的值作为字段名,在哈希表里新增一个字段,并赋值为true,轮到下一个元素时,先把该元素的值作为字段名在哈希表里查出相应的值来(如果哈希表中没有该值,得到的是undefined,这在if语句中与false效果相同)。然后作判断,true则表示该元素已存在,false则代表该元素不存在,那么就在哈希表里新增一个字段,设为true,以此循环。

可以看到这个算法高效的原因是只作了一层循环,原先查重用的内循环被哈希表替换了。这个方法对数据种类数的敏感程度很低,哪怕待处理数组所有的数据都不相同,它的速度也不会有大变化。但是由于用字段名访问字段值的效率要比用下标访问数组元素要低很多,所以,在数据种类数较少的时候,双循环的效率还是要高一点。

上面的只是一个思路而已,在代码实现上,还是有一些细节需要注意的,比如说哈希表的字段名,其实准确来说不能用元素的值来作字段名,因为值分为number类型和字符串类型,而在作字段名的时候,javascript会统一当做字符串处理,这样,就无法区分诸如123和“123”这样的变量了。因此,最好的处理方法是,利用typeof把值的类型求出来,然后用它和值本身拼接起来作为哈希表的字段名。代码如下:

 

最后,来上一下实验结果,提供一个参考方便大家针对自己的需求进行算法选择。

其中,表格中数据是计算时间,单位是ms。

在这个表里,可以看到双循坏的效率在数据重复量大的时候是很低的(注意上表中,重复率是百分比,因此数据量越大,效率变慢明显)。经过测试,数据的种类数低于50时(50是指50种数据,而不是50%),双循环有较好的效率。个人建议,在需要自定义需求的时候,还是使用Hash表比双循环好,因为双循环对数据的敏感高,稳定性差,不利于后期维护。当然啦,如果只是单纯的基本数据查重,而且没有特殊需求,应该使用效率最高的Set对象方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值