这是我参与更文挑战的第23天,活动详情查看: 更文挑战
前提
面试官在考察面试者的数组基础的时候,经常会问面试者一道题:
数组该如何去重?
这道题比较高频,因为它可以考察面试者对数组方法掌握的熟悉程度,以及思考能力和算法能力。今天咱们来看看,看一下能罗列出多少种方法。
数组去重
问题: 数组如何去重?
例子: [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
期望: [1, 2, 3, 5, 6, 4]
这里数组的元素主要是针对基本类型,引用类型暂不考虑。
双重遍历法
第一种实现:
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
// 因为splice删除后 后面元素往前移动了,j要减一
j--
}
}
}
console.log(arr) // [1, 2, 3, 5, 6, 4]
原理:
第一重遍历数组,在这个遍历里面,对数组再遍历,下标从i+1开始(因为如果重复只能是在i
之后的元素);
如果在第二重遍历中发现arr[i] === arr[j]
,则把该元素从j
处删除(也可以从i
处删除,不推荐,因为顺序会变),这里要注意的是arr删除后,j
也要减一(因为arr
删除后,后面的元素往前移动了一步)
第二种实现:
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let isDuplicate = false
let uniqueArr = []
for (let i = 0; i < arr.length; i++) {
isDuplicate = false
for (let j = 0; j < uniqueArr.length; j++) {
if (arr[i] === uniqueArr[j]) {
isDuplicate = true
}
}
// 没有重复,则把元素push到uniqueArr中
!isDuplicate && uniqueArr.push(arr[i])
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4]
原理:
设置一个标志位isDuplicate
, 让源数组跟去重后的数组的元素对比, 如果有相等的,则证明是重复了,标志位为true
, 不做任何处理。如果没有,则标志位是false
, 则把该元素push到去重后的数组中。
indexOf函数
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
for (let i = 0; i < arr.length; i++) {
if (arr.indexOf(arr[i]) === i) {
uniqueArr.push(arr[i])
}
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4]
原理:
主要是利用indexOf
函数,indexOf
函数返回的下标和遍历的下标i
,如果是相等的,则证明是没有重复的,然后push
到uniqueArr
中,不相等则代表之前出现过了,是重复的,则不处理。
include函数
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
for (let i = 0; i < arr.length; i++) {
if (!uniqueArr.includes(arr[i])) {
uniqueArr.push(arr[i])
}
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4]
原理:
主要是利用include
函数,include
函数返回布尔值,代表数组是否已经存在该元素,如果不存在则push
到uniqueArr
中,存在则代表uniqueArr
数组已经存在了,无需再push
了。
Set函数
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr= [...new Set(arr)]
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4]
原理:
主要是利用es6的Set函数,它的内部元素都是唯一的,没有重复值,最后用spread
操作符把它变成数组。
Map函数
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
let map = new Map()
for (let i = 0; i < arr.length; i++) {
if (!map.get(arr[i])) {
map.set(arr[i], true)
uniqueArr.push(arr[i])
}
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4]
原理:
主要是利用es6的Map
函数,可以用对象当键的键值对集合,如果Map
存在元素,则返回true
,则代表uniqueArr
数组已经存在该元素了,无需处理。
对象的hasOwnProperty
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
let obj = {}
for (let i = 0; i < arr.length; i++) {
if (!obj.hasOwnProperty(typeof arr[i] + arr[i])) {
obj[typeof arr[i] + arr[i]] = true
uniqueArr.push(arr[i])
}
}
console.log(uniqueArr) // [1, 2, 3, 5, 6, 4]
原理:
主要是利用对象的hasOwnProperty
函数,如果对象存在有待检验的key,那么hasOwnProperty(key)
就返回true
,否则返回false
。把数组的元素存进对象中,hasOwnProperty(元素)
如果返回true
, 则证明uniqueArr
已经存在该元素了,无需再push
了。
这里是用typeof arr[i] + arr[i]
做key
,主要是为了兼容数组可能出现字符串'1'
和数字1
被当作同一种值去重了。
sort排序
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = []
arr.sort()
for (let i = 0; i < arr.length; i++) {
if(arr[i] !== arr[i + 1]) {
uniqueArr.push(arr[i])
}
}
console.log(uniqueArr) // [1, 2, 3, 4, 5, 6]
原理:
主要是利用sort
函数进行排序,排序后然后对数组遍历,如果相邻元素不相等,则证明uniqueArr
还没有该元素,然后就可以push
进去。否则不处理。
reduce函数
let arr = [1, 2, 3, 3, 3, 2, 5, 5, 6, 4, 1]
let uniqueArr = arr.sort().reduce((acc, cur, i) => {
if (acc.length === 0 || acc[acc.length - 1] !== cur) {
acc.push(cur)
}
return acc
}, [])
console.log(uniqueArr) // [1, 2, 3, 4, 5, 6]
原理:
主要是利用reduce
函数,先排序,然后传入一个新数组,然后判断如果是新数组的length
等于0 或者新数组的元素和遍历元素对比,如果不等就则push
元素。
总结
整体的思路主要是这些:
- 双重遍历数组,发现相等的就剔除,接着遍历
- 新建一个新的空数组,遍历数组,如果空数组不存在该元素,则
push
到空数组中 - 利用对象/
Map
的key的不重复特性 - 利用
Set
的元素的唯一性 - 排序数组,然后遍历数组,左右相互对比,不相等则
push
到空数组中
推荐:
对象/Map
,Set
的方案好一些,复杂度不高,可以对NaN
去重,其它的方案复杂度会高一些。
以上就是我总结的数组去重的N种方法,大家可以都动手敲敲试试,理解了原理就不怕面试官问啦~