JavaScript-数组去重_java点击上方“前端极客技术”,轻松关注

JavaScript-数组去重_java_02

前言

JavaScript的数组去重是前端比较常见的一个问题,今天我们来学习几种常见的数组去重方法。

双层循环

大部分人最先想到的是通过双层循环来去重。双层循环的实现原理很简单:我们先新建一个空的数组res,通过遍历待去重数组array和res,判断array[i]是否存在于res中,如果不存在,将array[i]push到res中。

该方法虽然简单,但是其时间复杂度为 O(n^2),如果数组长度很大,那么会非常耗费内存。

我们直接看下面的代码:

var array = [1, 1, '1', '1', 2, 3, '2']
function unique(arr) {  var res = []  for (var i = 0; i < arr.length; i++) {    for (var j = 0, resLength = res.length; j < resLength; j++) {      if (arr[i] === res[j]) {        break      }    }    if (j === resLength) {      res.push(arr[i])    }  }  return res}
console.log(unique(array))   // [1, "1", 2, 3, "2"]

indexOf

针对上面的方法,我们可以利用 indexOf 来简化内层循环,将双层循环变为单层循环。实现代码如下:

var array = [1, 1, '1', '1', 2, 3, '2']
function unique(arr) {  var res = []  for (var i = 0; i < arr.length; i++) {    if (res.indexOf(arr[i]) === -1) {      res.push(arr[i])    }  }  return res}
console.log(unique(array))   // [1, "1", 2, 3, "2"]

sort()排序后相邻比对去重

如果我们先将数组进行排序,排序后相同的元素就会被排在一起,这样我们只要将当前元素和上一个元素进行比较,看两者是否相同,相同则说明重复,不相同push进res中。

实现代码如下:

let array = [1, 1, '1', '1', 2, 3, '2']
function unique(arr) {  let sortedArr = arr.concat().sort()  let res = []  for (let i = 0; i < sortedArr.length; i++) {    if (sortedArr[i] !== sortedArr[i-1]) {        res.push(sortedArr[i])    }  }  return res}
console.log(unique(array))   // [1, "1", 2, "2", 3]

filter

ES5 提供了filter方法,我们可以用来简化外层循环。indexOf和排序后去重分别使用filter方法的实现代码如下:

var array = [1, 1, '1', '1', 2, 3, '2']
// indexOffunction unique(arr) {  var res = arr.filter(function(item, index, arr) {    return arr.indexOf(item) === index  })  return res}console.log(unique(array))
// sortfunction sortedUnique(arr) {  return arr.concat().sort().filter(function(item, index, arr) {    return !index || item !== arr[index - 1]  })}

Set()和Map()

如果你熟悉ES6的话,就知道利用Set()和Map()两个数据结构,我们也可以实现数组去重。

我们先来看下Set()的实现代码:

var array = [1, 1, '1', '1', 2, 3, '2']
function unique(arr) {  return Array.from(new Set(arr))}console.log(unique(array))
// 另一种写法function unique(arr) {  return [...new Set(arr)]}

接下来我们来看下Map()的实现代码:

var array = [1, 1, '1', '1', 2, 3, '2']
function unique(arr) {  const tmp = new Map()  return arr.filter(item => !tmp.has(item) && tmp.set(item, 1))}

特殊类型比较

上面我们去重的数组包含的元素都是简单的string和number,但是数组的元素类型还可以是null、undefined、NaN、对象等,针对这些元素,用我们上面实现的方法是有问题的。

假设我们有下面这样一个数组需要去重,并且用上面的提到的方法去重,结果如下:

var array = ['true','true',true,true,0,0,1,1,'1','1',15,15,false,false,undefined,undefined,null,null,NaN,NaN,'NaN','NaN','a','a',{},{},{a:2},{a:2}];
// 去重结果如下:// 双层循环["true", true, 0, 1, "1", 15, false, undefined, null, NaN, NaN, "NaN", "a", {}, {}, {a:2}, {a:2}]
// indexOf["true", true, 0, 1, "1", 15, false, undefined, null, NaN, NaN, "NaN", "a", {}, {}, {a:2}, {a:2}]
// sort排序后去重[0, 1, "1", 15, NaN, NaN, "NaN", {}, {}, {a:2}, {a:2}, "a", false, null, "true", true, undefined]
// filter + indexOf["true", true, 0, 1, "1", 15, false, undefined, null, "NaN", "a", {}, {}, {a:2}, {a:2}]
// filter + sort [0, 1, "1", 15, NaN, NaN, "NaN", {}, {}, {a:2}, {a:2}, "a", false, null, "true", true, undefined]
// Set["true", true, 0, 1, "1", 15, false, undefined, null, NaN, "NaN", "a", {}, {}, {a:2}, {a:2}]
// Map["true", true, 0, 1, "1", 15, false, undefined, null, NaN, "NaN", "a", {}, {}, {a:2}, {a:2}]

将各种方法和去重结果整理成下面的表格:

方法去重结果
双层循环 ===判断对象和NaN没去重
indexOf对象和NaN没去重
filter + indexOf对象没去重,NaN被忽略掉了
filter + sort对象和NaN没去重
Set对象没去重
Map对象没去重

通过上面的表格,我们可以看出主要是对象和NaN两种类型的元素需要注意。

使用=== 判断两个NaN是否相等,得到的结果为false。

优化:包含对象的数组去重

如果元素是对象,我们利用Object键值对的方式来进行去重,利用JSON.stringify将对象序列化后存为Object的key值,比如:Object[value]=1,在判断另一个元素时,如果Object[value2]存在的话,说明该值重复。

如果元素不是对象,我们使用includes来判断新的数组res中是否有当前值。

实现代码如下:

var array = ['true','true',true,true,0,0,1,1,'1','1',15,15,false,false,undefined,undefined,null,null,NaN,NaN,'NaN','NaN','a','a',{},{},{a:2},{a:2}];
function unique(arr) {  var res = []  var obj = {}  arr.forEach(item => {    if (typeof item !== 'object') {      if (!res.includes(item)) {        res.push(item)      }    } else {      var valueStr = JSON.stringify(item)      if (!obj[valueStr]) {        res.push(item)        obj[valueStr] = 1      }    }  })  return res}console.log(unique(array))// ["true", true, 0, 1, "1", 15, false, undefined, null, NaN, "NaN", "a", {}, {a:2}]

总结

数组去重是开发中经常会碰到的一个问题,我们可以根据不同的应用场景来选择不同的实现方式。如果数组中不包含对象,我们可以直接使用Set进行去重,如果存在对象,则可以选择最后Object键值对的方式。