Javascript数组去重的方法

这个是个经常用来面试的题目(别小看这个题)考点有二:

  1. 正确性:考虑到javascript经常要在浏览器上运行,在不同浏览器环境下要保证一个函数的正确性可不是件简单的事情。
  2. 高性能:虽然大部分情况下javascript语言本身(狭义范畴,不包含DOM等延拓)不会导致性能问题,但是很不幸这是道考题,因此面试官还是会把性能作为一个考点。

    var arr = [1,2,1,'2',3,4,1,4,'2',{a:1},{b:1,c:2},{a:1}];
    

下面的方法测试结果都是对arr数组进行测试的。

第一反应方案———扩展方法实现

  1. 利用indexOf判断旧数组

     Array.prototype.unique1 = function () {
      var newArray = [];
      for(var i=0 ;i < this.length;i++){
          //判断新数组里的是否存在该值
          if(newArray.indexOf(this[i]) == -1){
              newArray.push(this[i]);
          }
      }
         return newArray;
    };
    console.log(arr.unique1()); 
    //结果 :[ 1, 2, '2', 3, 4, { a: 1 }, { b: 1, c: 2 }, { a: 1 } ]
    
  2. 利用indexOf判断新数组

    1
    	Array.prototype.uniqu2 =  function () {
    	      var newArray = [];
    	      for (var i = 0; i < this.length; i++) {
    	          //判断当前数组的第i项在当前数组中值第一次出现的位置是不是=i,如果!=i,表示有重复,忽略掉。
    	          if (this.indexOf(this[i]) == i) {
    	              newArray.push(this[i]);
    	          }
    	      }
    	      return newArray;
    	  };
    	  
    	  console.log(arr.unique2()); 
    //结果 :[ 1, 2, '2', 3, 4, { a: 1 }, { b: 1, c: 2 }, { a: 1 } ]

以上两种方法都觉得很靠谱,在现代浏览器下,上面这个函数很正确,性能也不错。但前端最大的悲哀也是挑战之处在于,要支持各种运行环境。在 IE6-8 下,数组的 indexOf 方法还不存在,那怎么办呢?接下来就利用到了对象了。

  1. 利用hash对象key的唯一性—————优化方案

    Array.prototype.unique3 = function () {
     var newArray = [], hash = {};
     // for循环时,每次取出一个元素与对象进行对比
     // 如果这个元素不重复,则将它存放到newArray数组中
     // 同时把这个元素的内容作为对象的一个属性,并赋值为1,
     // 存入到第2步建立的对象中
     for(var i =0 ;i<this.length;i++){
         // 检测在hash对象中是否包含遍历到的元素的值
         //(typeof this[i])+this[i] 为了处理number与string类型,
         // 但是如果数组里包含object类型的对象(如:{a:1})此方法不可行
         if(!hash[(typeof this[i])+this[i]]){
             // 如果不包含,存入object对象中该属性名的值设置为1
             hash[(typeof this[i])+this[i]] = true;
             // 如果不包含,将存入对象的元素的值推入到newArray数组中
             newArray.push(this[i]);
         }
     }
     return newArray;
     };
    console.log(arr.unique()); 
     //结果 :[ 1, 2, '2', 3, 4, { a: 1 } ]  
    

    核心是构建了一个 hash 对象来替代 indexOf. 注意在 JavaScript 里,对象的键值只能是字符串,因此需要 var key = typeof(this[i]) + this[i] 来区分数值 2 和字符串 ‘2’ 等情况。

    但优化真的很容易带来坑,比如上面的实现,对下面这种输入就无法判断:

    unique([ new String(2), new Number(2) ])
    

    可以继续修改代码,做到性能和正确性都很好。但往往,这带来的结果并不好。如果有谁有好的方法解决这个问题,可以回复我。

使用Set

  • SetMap是ES6中新增的数据结构
  • Set直接可以存储不重复的一组key,这个key也可以是对象,字符串等。

    // ES6 
    function unique (arr) { 
        const seen = new Map()     
        return arr.filter((a) => !seen.has(a) && seen.set(a, 1));
     } 
    // or
    function unique (arr) { 
         return Array.from(new Set(arr));
    }
    

##创建set

var s = new Set([1,2,3,4,5]);
console.log(s); //Set { 1, 2, 3, 4, 5 }

新增元素

s.add(6);
console.log(s); //Set { 1, 2, 3, 4, 5, 6 }
s.add(3); //重复的不会增加
 console.log(s);//Set { 1, 2, 3, 4, 5, 6 }

删除元素

s.delete(3);
console.log(s);//Set { 1, 2, 4, 5 }

遍历元素

  • SetMap无法使用下标
  • ES6标准引入了新的iterable类型,ArrayMapSet都属于iterable类型。

    for(var x of s){

    console.log(x);
    

    }

  • 或者直接使用iterable内置forEach方法

  • forEach方法ES5.1标准引入的

    s.forEach(function(ele,set){
        console.log(ele);
    });
    
文章目录
  1. 1. 第一反应方案———扩展方法实现
  2. 2. 使用Set
    1. 2.1. 新增元素
    2. 2.2. 删除元素
    3. 2.3. 遍历元素