说到去重有很多的实现,在ES6下根本不是问题,然而在ES6之前……习惯性的思路是 copy 一个新的集合并与原始集合做遍历、比对,类似如下这种实现方法:
var uniq1=function(arr){
var temp=[],
item,
i,n;
for(i=0;i<arr.length;i+=1){//开始去重
item=arr[i];
temp.push(item);
for(n=0;n<temp.length;n+=1){
if(item===temp[n-1]){//如temp与arr中的元素重复则删除
temp.splice(temp.length-1,1);
}
}
}
return temp;
};
上面这个例子会创建一个临时的空数组对象,在将元素逐个放入其中时与前一个元素作比对,如果值相同则从临时数组中剔除,直至遍历完所有比对对象……这个方法尽可能的优化了性能,但是出现了嵌套循环,代码实现逻辑复杂、对部分阅读者并不友好对吗?下面介绍一种或许可以减压并且性能表现不俗的实现方法。
先说说思路,我们习惯对数组元素逐一比较后取得不重复值的方式来实现去重,这让编程者思维局限在数组里。其实js世界为我们提供了现成的去重偏方——对象{},因为对象的键名是唯一的,我们利用这一特性,把值作为对象的键名就可以实现去重,于是:
var uniq2=function(arr){
var i,temp=[],
key,newArr=[];
for(i=0;i<arr.length;i+=1){//遍历数组,将值作为键名放入对象中
temp[arr[i]] = (temp[arr[i]]+1)||1;//对象值随便赋,用作重复次数的统计是个不错的选择
}
for(key in temp){//把生成的临时对象转回数组
newArr.push(key);
}
return newArr;
};
逻辑清晰,效果很好。由于是直接对对象键名的覆盖,无需比对重复值,循环体的运算复杂度大大减少,这在做大量数据操作时对性能的提升会很明显,因为无论如何仅仅是对数组的一次遍历而已。我们写个简单的测试得到如下结果:
优势明显!但是带来一个新问题,我们遍历结果发现所有的值都变成了字符串!还有,如果是对象集合的去重呢?我们知道对象的键名是字符串,不能直接用对象字面量,真是没有银弹!怎么办?我们依旧沿袭“偷懒的作风”,用js给我们提供的现成方法JSON.stringify()把对象转成字符串,在去重完成后用JSON.parse()方法转回对象字面量……好吧,这太容易了,所以,我再偷下懒,完整的实现代码就由各位自由发挥吧,很容易不是吗。
据说写个ES6代码在用Babel转换下不就好了……然而用户需求是无止境的依然有人不放弃对<ie8的支持。并且我发现第二种方法即使加上JSON的解析,在性能上依然优于[...new Set(arr)]
:
可能这点数据量还不足以体现这种方法的巨大优势,我们加点料再测一次:
完胜啊!
关于去重还有很多话题可以展开,比如算法、对结果排序的影响等,但归根结底还是性能优化、高效处理。这是个很有趣味的主题!