Js数组元素去重问题(吐血整理)十一种方法

前段时间遇到数组去重的问题,网上查到的资料也是参差不齐,有的实用有的简洁有的好记,所以在此把遇到的一些方法做一个记录,同时对一些常用方法时间消耗、以及难易程度做个比较。

Ps所有代码均在Firefox内做过测试。

 

一、利用es6中的set去重

set 是es6中新的数据结构,类似于数组,但其成员唯一,没有重复的值,无序。一般用[...myset]的形式转化为数组之后遍历。优点是代码简洁方便记忆速度也较快,缺点是……额还没发现缺点,要不然为啥放第一个23333

function func(arr){
    return Array.from(new Set(arr));
}
var arr=[1,2,3,4,4,4,NaN,NaN,'NaN',true,'true',null,null,{},{},{}];
console.log(func(arr));

这是在Firefox浏览器内测试的结果,注意看到还无法去掉空对象

二、使用map

Map是js中的键值对结构,其查找速度极快。用其排序原理即遍历数组,向map内添加键值对,由于键不允许存在重复,自然利用map存储的元素也不会重复了。优点是效率极高,占用空间也较小,缺点是需要熟练掌握map用法。

var funcmap = function(arr){
    let temp = new Map();
    return arr.filter(item=>{return !temp.has(item) && temp.set(item,1);});
}

同上set,一样无法去掉空对象

三、使用对象属性无重复来对数组去重

此法相比上个方法由于是向对象内添加属性,其操作速度自然不如以上,但可以去除空对象,以及类似于字符串NaN,字符串true这种。(后文解释为什么会去除字符串NaN,字符串true)

var funcobject = function(arr) {
  var array = [];
  var obj = {};
  for (var i = 0; i < arr.length; i++) {
    if (!obj[arr[i]]) {
      array.push(arr[i]);
      obj[arr[i]] = 1;
    } else {
      obj[arr[i]]++;
    }
  }
  return array;
};

四、利用sort去重

Sort去重原则上还是判断是否相等。相比上面键值对,对象属性等骚操作速度自然是不如了,不过sort支持度广泛,容易理解,排序速度也不是太慢。

var funcsort = function(arr) {
  arr.sort();
  var array = [];
  for (var i = 1; i < arr.length; i++) {
    if (arr[i] !== arr[i - 1]) {
      array.push(arr[i - 1]);
    }
  }
  return array;
};

对于空对象,涉及到js底层对对象的比较等,有兴趣可以再行研究~这里贴一张结果的图片

五、利用indexof去重

Es5中提供的方法indexof,根据元素判断是否在数组中,类似于上面的方法,优点好理解记忆,方法直观清晰。缺点对ie支持不好(ie:我不背锅哈)具体可查ie浏览器对es5的支持程度,还有速度实际是更慢的,因为循环和比较相比上面做的多了。

var funcindexof = function(arr) {
  var array = [];
  for (var i = 0; i < arr.length; i++) {
    if (array.indexOf(arr[i]) === -1) {
      array.push(arr[i]);
    }
  }
  return array;
};

六、使用splice去重

嵌套两层循环,寻找相同元素,如果相同则删除。

与上个类似,可以理解为上个方法的不同实现,同样要考虑到ie对splice的支持,其速度理论上还不如上面的indexof。

var funcsplice = function(arr) {
  for (var i = 0; i < arr.length; i++) {
    for (var j = i + 1; j < arr.length; j++) {
      if (arr[i] == arr[j]) {
        arr.splice(j, 1);
        j--;
      }
    }
  }
  return arr;
};

七、filter结合indexof去重

该方法优势在于,不输于第一个方法的简洁,仅用一行代码就可处理,巧妙结合filter,indexof等数组方法的特性。不过注意indexof的性能较低,处理几百个元素还行,如果在性能差的移动端处理上千元素,就会出现阻塞页面的卡顿。

var funcfilter = function(arr) {
  return arr.filter((item, index) => arr.indexOf(item, 0) === index);
};

相比上面方法五的indexof为啥少了两个NaN,原因如下:

 

接下来就是几个不怎么常用的方法了,用来在面试官面前装逼拓宽知识面用的

八、利用includes去重

Includes方法检测数组是否包含该元素,返回值是true或者false,和indexof有异曲同工之妙~

var funcincludes = function(arr) {
  var array = [];
  for (var i = 0; i < arr.length; i++) {
    if (!array.includes(arr[i])) {
      array.push(arr[i]);
    }
  }
  return array;
};

同样是对NaN处理的不同,使得includes方法可以去除多余的NaN,如果有这样的需求可以利用此方法

这样看差别应该就很明显了吧

九、 利用reduce去重

reduce() 是数组的归并方法,与forEach()、map()、filter()等迭代方法一样都会对数组每一项进行遍历,但是reduce() 可同时将前面数组项遍历产生的结果与当前遍历项进行运算,这一点是其他迭代方法无法企及的。Reduce函数形式如下

arr.reduce(function(prev,cur,index,arr){
...
}, init); 

其中arr 表示原数组;

prev 表示上一次调用回调时的返回值,或者初始值 init;

cur 表示当前正在处理的数组元素;

index 表示当前正在处理的数组元素的索引,若提供 init 值,则索引为0,否则索引为1;

init 表示初始值。

是不是看起来很复杂,但其实实际使用一般只用到prev和cur,分别对应previous上一个返回值和current当前值。

var funcreduce = function(arr) {
  return arr.reduce((prev, cur) => {
    prev.indexOf(cur) === -1 && prev.push(cur);
    return prev;
  }, []);
};

实际上还是如上indexof的变种。

十、reduce和includes结合去重

var funcreduceincludes = function(arr) {
  return arr.reduce((prev, cur) => (prev.includes(cur) ? prev : [...prev, cur]),[]);
};

一行代码搞定,极客首选之一

本质上还是includes,只不过由reduce帮忙遍历而已

可以看到结果同上includes

十一、使用hasOwnProperty去重

hasOwnProperty是用来检测对象是否包含某一属性的方法。区别于中括号,hasOwnProperty不会对原型链上端的对象进行属性判断。所以与上面“使用对象属性不重复去重”略有小小区别(当然别瞎修改原型链就没问题)

var funchasOwnProperty = function(arr) {
  var array = [];
  var temp = {};
  for (var i = 0; i < arr.length; i++) {
    if (!temp.hasOwnProperty(arr[i])) {
      temp[arr[i]] = 1;
      array.push(arr[i]);
    }
  }
  return array;
};

看到与上面对象属性去重之后结果是一致的。

其实本质上,js在内部声明某个对象的属性时,会做一个toString的操作,这也就解释了为啥NaN和‘NaN’会被去重,同样的,如果打印观察的话,空对象{ }在做对象的属性名时也会转化为"[object Object]"。

 

总结:

总体上来看,去重的思路大概就是三种

一是利用键值对里键不重复

二是利用对象里属性名不可重复

三则是直接对元素比较判断

而去重的工具无非就是各种遍历和查找的方法,在这些方法之上结合上面的思路,也就完成了数组去重。

方法不论好坏,也不仅限于上面有限种类,根据业务需求来实现代码,才是最好的数组去重方法~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章