美文网首页
[js]从 ES3 到 ES6 教你如何数组去重

[js]从 ES3 到 ES6 教你如何数组去重

作者: 清水芦苇 | 来源:发表于2017-08-16 22:05 被阅读34次

    声明

    以下方法仅对数组值全部属于 primitive data type 的情况有效。

    ES6

    方法一: Set数据结构 + Array.from静态方法

    ES6中新增了Set数据结构,类似于数组,但是它的成员都是唯一的 ,其构造函数可以接受一个数组作为参数,如:

     let array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3];
     let set = new Set(array);
     let deduped = Array.from(set);// `deduped = de + dup(duplication) + ed`
     console.log(deduped);
     // [1, 2, 3, 4, 5]
    

    方法二:Set数据结构 + 扩展语法(spread syntax)

    let array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3];
    let deduped = [...new Set(array)];
    console.log(deduped);
     // [1, 2, 3, 4, 5]
    

    注意扩展语法对所有可遍历对象均有效

    let obj = {'key1': 'value1'};
    let array = [...obj]; 
    // TypeError: obj is not iterable
    

    方法三: 箭头函数+es5语法(filter, indexOf)

    let array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3];
    let deduped = array.filter((el,i,arr) => arr.indexOf(el) === i);
    console.log(deduped);
     // [1, 2, 3, 4, 5]
    

    ES5

    var array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3];
    var deduped = array.filter(function(el,i,arr) {
        return arr.indexOf(el) === i;
    })
    console.log(deduped);
     // [1, 2, 3, 4, 5]
    

    lt_ES5

    var array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3,[1,2],[3,4]];
    var deduped = [];
    for(var i=0, l=array.length;i<l;i++){
        var tmp = array[i];
        deduped.indexOf(tmp) === -1 && deduped.push(tmp);
    }
    console.log(deduped);
     // [1, 2, 3, 4, 5]
    

    知识补充——关于indexOf

    关于浏览器兼容性

    在 IE6-8 下,数组的 indexOf 方法还不存在。
    所以如果需要兼容 IE6-8,得自己造一个轮子:

    var indexOf = [].indexOf ?
        function(arr, item) {
          return arr.indexOf(item)
        } :
        function indexOf(arr, item) {
          for (var i = 0; i < arr.length; i++) {
            if (arr[i] === item) {
              return i
            }
          }
          return -1
        }
    

    关于性能——时间复杂度

    如果说利用 set 数据结构是一种作弊的方式,那么用 indexOf 就是一种相对低下性能的方式,因为 indexOf 的底层也是一次遍历。嵌套循环会让追求性能极致的人感觉不爽。

    ① arr 遍历push 到新数组
    ② 其中每循环一次调用数组的 indexOf 方法,又会是一次遍历
    所以,相当于两次循环嵌套,遍历复杂度为O(n2)

    这种情况下,为追求性能,更好的实现方式为:利用对象 key 的唯一性处理。首先将数组转换为对象,其次将对象转换为去重后的数组。
    比如:

    var songs = [
             {name:"都选C",artist:"大鹏"},
             {name:"都选C",artist:"大鹏"},
             {name:"塑料袋",artist:"乔杉"},
             {name:"塑料袋",artist:"乔杉"},
             {artist:"乔杉",name:"塑料袋"}
         ];
    
    function unique(songs){
      let result = {};
      let finalResult=[];
      // 数组转换为对象,利用对象key的唯一性去重
      songs.forEach(function(song, i){
        result[song.name]=song;
      })
      // 将对象还原为去重后的数组
      Object.keys(result).forEach(function(key, i){
        finalResult.push(result[key]);
      })
      
      return finalResult;
    }
    
    console.log(unique(songs));
    
    // 注明:偷了个懒 forEach 的性能比 for 循环慢好多。
    // 参考:https://github.com/jawil/blog/issues/2
    

    2017-11-16 更新。实际上这个用了至少两次循环(虽然不是循环嵌套),但仍然是低效的。因为最简单的数组去重只需要用一层循环即可。

    更为复杂的案例可以看笔者写的这个demo

    参考资料

    https://github.com/lifesinger/blog/issues/113

    相关文章

      网友评论

          本文标题:[js]从 ES3 到 ES6 教你如何数组去重

          本文链接:https://www.haomeiwen.com/subject/ydrwrxtx.html