JS实现数组去重方法整理

前言

我们先来看下面的例子,当然来源与网络,地址《删除数组中多个不连续的数组元素的正确姿势

我们现在将数组中所有的‘ a’ 元素删除:

var arr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];
arr.forEach(function(value, index) {
    value === 'a' ? arr.splice(index, 1) : '';
})
console.log(arr);
//["a", "b", "c", "d", "a", "e", "g", "f"]

只要相邻的‘ a’ 元素, 都没被删除, splice不但可以删除元素本身, 还同时可以减少数组长度( 就是抹去一切痕迹),
这样导致后续的数组元素会代替已经删除的元素的位置, 但是循环是按照数组的下标按顺序删除, 这样就会漏掉迁移的元素。

看到网上有网友在说使用delete进行操作,如下:

var arr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];
arr.forEach(function(value, index) {
   value === 'a' ? delete arr[index] : '';
})
console.log(arr); //[2: "b", 3: "c", 4: "d", 7: "e", 8: "g", 10: "f"]

但是得到的arr其实是一个非常规的数组了,也就是说其实delete主要是用于对对象属性的操作。这确实要根据自己的需求来了。

当然简单的实现如下:

var arr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];
var newArr = arr.filter(function(key) {
    return key !== 'a'

})
console.log(newArr); //["b", "c", "d", "e", "g", "f"]

 下面总结下常用实现方式。

性能测试模板

let arr1 = Array.from(new Array(100000),(item,index) => {
    return index;
})

let arr2 = Array.from(new Array(50000),(item,index) => {
    return index + index;
})

function distinct(a,b) {
    // 数组去重
}
console.time('去重计算时间');
console.log('去重后的长度', distinct(arr1, arr2).length);
console.timeEnd('去重计算时间');

上面创建了两个数组长度,1个10W,1个5W长度的数组,合并到一个数组,然后进行去重,验证下去重的计算时间是多长。

方法一:Array.filter() + indexOf

function distinct(a,b) {
    let arr = a.concat(b);
    return arr.filter((item,index) => {
        return arr.indexOf(item) === index;
    })
}

思路就是ES6 中的 Array.filter() 遍历数组,并结合 indexOf 来排除重复项。看下结果:

从截图可以看出,计算时间花费了19753ms的时间。

方法二:使用 for...of + includes()

function distinct(a,b) {
    let arr = a.concat(b)
        let result = []
        for (let i of arr) {
            !result.includes(i) && result.push(i)
        }
        return result
}

这种方式跟Array.filter() + indexOf实现思路差不多,结果如图所示:

从图上可以看出,计算的时间跟Array.filter() + indexOf的时间差不多。

方法三:双重 for 循环

function distinct(a, b) {
    let arr = a.concat(b);
    for (let i=0, len=arr.length; i<len; i++) {
        for (let j=i+1; j<len; j++) {
            if (arr[i] == arr[j]) {
                arr.splice(j, 1);
                // splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一
                len--;
                j--;
            }
        }
    }
    return arr
}

优点:简单易懂
缺点:占用内存高,速度慢

看下结果:

方法四:Array.sort()

首先使用 sort() 将数组进行排序

然后比较相邻元素是否相等,从而排除重复项。

 

function distinct(a, b) {
    let arr = a.concat(b)
    arr.sort(function(a,b){        //对数组进行排序才能方便比较
        return a - b;
    })
    let result = [arr[0]]

    for (let i=1, len=arr.length; i<len; i++) {
        arr[i] !== arr[i-1] && result.push(arr[i])
    }
    return result
}

 

从上面代码可以看出来,做了一次排序和一次循环,所以效率比前面都高,结果如图所示:

可以看出,结果只花了255ms左右时间。

方法五:for...of + Object

利用对象的属性不能相同的特点进行去重,代码如下:

function distinct(a, b) {
    let arr = a.concat(b)
    let result = []
    let obj = {}

    for (let i of arr) {
        if (!obj[i]) {
            result.push(i)
            obj[i] = 1
        }
    }

    return result
}

执行结果如图所示:

结果只花费了43ms左右时间。

方法六:ES6的new Set()

function distinct(a, b) {
    return Array.from(new Set([...a, ...b]))
}

Set数据结构,它类似于数组,其成员的值都是唯一的。

利用Array.from将Set结构转换成数组。

结果如图所示:

从结果看出来该方法执行只花了151ms左右的时间。

总结

从上面几种方法得出如下结论:

  • 数组去重要么使用for...of + Object方式,要么使用ES6的 new Set()方式。
  • 从执行结果看for...of + Object的效率应该是最高的(只在当前量级的计算结果来看)。

附录

附录中添加浏览器Array对象支持indexOf和forEach的polyfill:

Array.prototype.indexOf = Array.prototype.indexOf || function(item) {
    for (var i = 0, j = this.length; i < j; i++) {
        if (this[i] === item) {
            return i;
        }
    }
    return -1;
}

Array.prototype.forEach = Array.prototype.forEach || function(callback, thisArg) {
    if (!callback || typeof callback !== 'function') return;

    for (var i = 0, j = this.length; i < j; i++) {
        callback.call(thisArg, this[i], i, this);
    }
}

 

posted @ 2017-07-14 09:12  风雨后见彩虹  阅读(3299)  评论(2编辑  收藏