需求描述:为Array类型添加公共方法Unique,去掉给定数组中的重复元素。
五种方法:
/*方法一*/
//为Array类型的prototype对象添加unique1方法
Array.prototype.unique1=function(){
//遍历当前数组对象中的每个元素,同时创建新空数组对象,保存在变量n中
for(var i=0,n=[];i<this.length;i++){
n.indexOf(this[i])==-1&&n.push(this[i]);
}
return n;
}
/*方法二*/
//为Array类型的prototype对象添加unique2方法
Array.prototype.unique2=function(){
//遍历当前数组中每个元素,同时创建一个hash对象和一个结果数组
for(var i=0,hash={},r=[];i<this.length;i++){
!hash[this[i]]&&(hash[this[i]]=null,r.push(this[i]));
}
return r;
}
/*方法三*/
//为Array类型的prototype对象添加unique3方法
Array.prototype.unique3=function(){
//先将数组元素默认按升序排列
this.sort();
//遍历数组中每个元素,同时创建结果数组r,先放入原数组第一个元素。
for(var i=1,r=[this[0]];i<this.length;i++){
//用原数组当前元素和结果数组最后一个元素比较,如果不相等,就将原数组当前元素压入结果数组
this[i]!==r[r.length-1]&&r.push(this[i]);
}
return r;
};
/*方法四:*/
//为Array类型的prototype对象添加unique4方法
Array.prototype.unique4=function(){
//以空数组作为基础,依次迭代处理数组中每个元素。如果当前元素c在p数组中未出现过,则将当前元素c压入p数组。
return this.reduce(function(prev, curr) {
prev.indexOf(curr)<0&&prev.push(curr);
return prev;
}, []);
};
/*方法五:*/
//为Array类型的prototype对象添加unique5方法
Array.prototype.unique5 = function () {
//先将数组排序,再用两个逗号拼接数组元素,再用正则表达式将多个逗号分隔的相同的子字符串替换为仅剩一个第一个。最后按两个逗号为分隔,将字符串再切割成数组。
return this.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").split(",,");
}
/*测试代码:*/
window.onload=function(){
//获得输出结果的div
var out=document.getElementById("out");
//随机生成10000个0~10000之间的随机数,放入数组arr中
for(var i=0,arr=[];i<10000;i++){
arr.push(Math.floor(Math.random()*10000));
}
//获得开始时间:
var start=new Date();
//调用数组类型的去重复方法1,获得新数组保存在变量re中
var re=arr.unique1();
//获得结束时间:
var end=new Date();
//在控制台输出数组内容:
console.log(re);
//在输出div中写入方法和耗时
out.innerHTML+="方法1 耗时 "+(end-start)+" ms<br>";
//获得开始时间:
start=new Date();
//调用数组类型的去重复方法2,获得新数组保存在变量re中
re=arr.unique2();
//获得结束时间:
end=new Date();
//在控制台输出数组内容:
console.log(re);
//在输出div中写入方法和耗时
out.innerHTML+="方法2 耗时 "+(end-start)+" ms<br>";
//获得开始时间:
start=new Date();
//调用数组类型的去重复方法3,获得新数组保存在变量re中
re=arr.unique3();
//获得结束时间:
end=new Date();
//在控制台输出数组内容:
console.log(re);
//在输出div中写入方法和耗时
out.innerHTML+="方法3 耗时 "+(end-start)+" ms<br>";
//获得开始时间:
start=new Date();
//调用数组类型的去重复方法4,获得新数组保存在变量re中
re=arr.unique4();
//获得结束时间:
end=new Date();
//在控制台输出数组内容:
console.log(re);
//在输出div中写入方法和耗时
out.innerHTML+="方法4 耗时 "+(end-start)+" ms<br>";
//获得开始时间:
start=new Date();
//调用数组类型的去重复方法5,获得新数组保存在变量re中
re=arr.unique5();
//获得结束时间:
end=new Date();
//在控制台输出数组内容:
console.log(re);
//在输出div中写入方法和耗时
out.innerHTML+="方法5 耗时 "+(end-start)+" ms<br>";
}
测试代码:
/*测试代码:*/
window.onload=function(){
… …
//在输出div中写入方法和耗时
out.innerHTML+="方法1 耗时 "+(end-start)+" ms<br>";
//获得开始时间:
start=new Date();
//调用数组类型的去重复方法2,获得新数组保存在变量re中
re=arr.unique2();
//获得结束时间:
end=new Date();
//在控制台输出数组内容:
console.log(re);
//在输出div中写入方法和耗时
out.innerHTML+="方法2 耗时 "+(end-start)+" ms<br>";
}
对比前两种方法:明显第二种方法效率更高。主要原因:数组类型的indexOf方法其实也需要遍历数组中每个元素,才能找到匹配的元素。这就大幅增加了循环和比较的次数。而方法二中利用了JavaScript语言中对象底层其实是hash数组的特性。hash数组中,按照key查找元素,无需遍历。所以自然在循环次数上优于方法一。但是方法二却多创建了一个对象。多占用了空间,却换来了执行效率上的优化,这种现象也称为用空间换时间。
方法三比方法二效率稍低。因为方法三需要先对数组排序。无论采用何种方法排序,都会增加遍历和比较的次数。但即使如此,方法三远优于方法一。因为数组的sort方法采用的排序算法通常都经过了特别的优化,比indexOf这样的遍历方法效率要高。
方法五的执行效率与方法三相当。因为都采用了sort方法先排序再处理。另外,由此还可看出正则表达式处理字符串的效率和数组遍历的效率几乎相当。可见正则表达式的效率也是非常高的。
结论:查找最快的数据结构是hash数组。经常用于统计元素数量,去除重复元素等需要反复遍历和比较的操作中。而indexOf方法的效率不高,不推荐作为首选方法。
ES6中:
// 去除数组的重复成员
[...new Set(array)]