
面试前端必须准备的一个问题:怎样去掉Javascript的Array的重复项。据我所知,百度、腾讯、盛大等都在面试里出过这个题目。 这个问题看起来简单,但是其实暗藏杀机。 考的不仅仅是实现这个功能,更能看出你对计算机程序执行的深入理解。
我总共想出了三种算法来实现这个目的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | Array.prototype.unique1 = function()
{
var n = [];
for(var i = 0; i < this.length; i++)
{
if (n.indexOf(this[i]) == -1) n.push(this[i]);
}
return n;
}
Array.prototype.unique2 = function()
{
var n = {},r=[];
for(var i = 0; i < this.length; i++)
{
if (!n[this[i]])
{
n[this[i]] = true;
r.push(this[i]);
}
}
return r;
}
Array.prototype.unique3 = function()
{
var n = [this[0]];
for(var i = 1; i < this.length; i++)
{
if (this.indexOf(this[i]) == i) n.push(this[i]);
}
return n;
}
|
其中第1种和第3种方法都用到了数组的indexOf方法。此方法的目的是寻找存入参数在数组中第一次出现的位置。很显然,js引擎在实现这个方法的时候
会遍历数组直到找到目标为止。所以此函数会浪费掉很多时间。
而第2中方法用的是hash表。把已经出现过的通过下标的形式存入一个object内。下标的引用要比用indexOf搜索数组快的多。
为了判断这三种方法的效率如何,我做了一个测试程序,生成一个10000长度的随机数组成的数组,然后分别用几个方法来测试执行时间。
结果表明第二种方法远远快于其他两种方法。 但是内存占用方面应该第二种方法比较多,因为多了一个hash表。这就是所谓的空间换时间。
就是这个测试页面,你也可以去看看。
根据hpl大牛的思路,我写了第四种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Array.prototype.unique4 = function()
{
this.sort();
var re=[this[0]];
for(var i = 1; i < this.length; i++)
{
if( this[i] !== re[re.length-1])
{
re.push(this[i]);
}
}
return re;
}
|
这个方法的思路是先把数组排序,然后比较相邻的两个值。 排序的时候用的JS原生的sort方法,JS引擎内部应该是用的快速排序吧。 最终测试的结果是此方法运行时间平均是第二种方法的三倍左右,不过比第一种和第三种方法快了不少。
第五种方法
最近在做【搜索历史记录】功能也用到,开始用了 indexOf 方法,该方法在 ECMA5才有支持,对于 IE8- 就不支持了。
我们可以自己写一个函数(Array对象的方法都是定义在原型对象上的),如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | Array.prototype.unique = function(){
var length = this.length;
if(length <= 1){
return this;
}
if(!Array.prototype.indexOf){
Array.prototype.indexOf = function(item){
var l = this.length, i = 0, r = -1;
if(l <= 0){
return -1;
}
for(; i < l; i++){
if(this[i] === item){
r = i;
}
}
return r;
}
}
var result = [];
for(var i = 0; i < length; i++){
if(result.indexOf(this[i]) === -1){
result.push(this[i]);
}
}
return result;
}
|
第六种方法
Array类型并没有提供去重复的方法,如果要把数组的重复元素干掉,那得自己想办法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function unique(arr) {
var result = [], isRepeated;
for (var i = 0, len = arr.length; i < len; i++) {
isRepeated = false;
for (var j = 0, len = result.length; j < len; j++) {
if (arr[i] == result[j]) {
isRepeated = true;
break;
}
}
if (!isRepeated) {
result.push(arr[i]);
}
}
return result;
}
|
总体思路是把数组元素逐个搬运到另一个数组,搬运的过程中检查这个元素是否有重复,如果有就直接丢掉。从嵌套循环就可以看出,这种方法效率极低。我们可以 用一个hashtable的结构记录已有的元素,这样就可以避免内层循环。 |