0%

数组去重的几种方法

今天记录下数组去重的几种方法,主要是思路。自觉在算法和数据结构方面比较薄弱,以后要注意这方面的学习。

1. 利用ES6的Set

ES6提供了一种新的数据结构Set,这种数据结构类似于数组,但是成员的值都是唯一的,不管是基本类型的值还是引用类型的值。学习更多关于Set的知识可以参考ECMAScript6入门一书

Set是一个构造函数,它可以接受一个数组作为参数,返回的Set实例对象的成员都是唯一的。利用Array.from()方法我们可以把Set对象转换为数组。所以通过Set这个“中转站”我们就达到了数组去重的目的。

1
2
var arr = [1, 2, 3, 1, 2, 3]
Array.from(new Set(arr)) // [1, 2, 3]

2. 使用includes或indexOf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var arr = [1, 2, 3, 1, 2, 3]

function unique(arr) {
var res = []

while (arr.length) {
var item = arr.shift()
// if (arr.indexOf(item) === -1) {
if (!arr.includes(item)) {
res.push(item)
}
}

console.log(res)
return res
}

unique(arr) // [1, 2, 3]

上面的代码先声明一个空数组作为最终要返回的结果数组,然后从数组头部遍历数组,判断当前遍历的项,如果结果数组中不存在该项,则push进结果数组,否则继续遍历。

3. 使用双层for循环嵌套,相同项删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var arr = [1, 2, 3, 1, 2, 3]
function unique(arr) {
var array = [...arr]
for (var i = 0; i < array.length; i++) {
for (var j = i + 1; j < array.length; j++) {
if (array[i] === array[j]) {
array.splice(j, 1)
}
}
}

console.log(array)
return array
}

unique(arr) // [1, 2, 3]

这种方法的思路也是比较清晰的,两层for循环,判断两次for循环中当前遍历的项是否相等,如果相等,则用splice方法删除内层for循环的当前项。需要注意的几点是:(1)内层for循环的起始索引应该使用外层索引+1,因为相同的项不需要比较。(2)这里使用全等操作符===来判断来个数组项是否重复是不够严谨的,首先NaN === NaN返回的是false,判断NaN应该用isNaN()方法;其次对象是否重复也要看具体要求,是要求对象的引用地址一致才算相同,还是说只要对象的属性一致就算相同要具体需求具体分析。在这里只是记录一下思路,就不做多余的判断了。(3)splice方法会修改原数组,所以最好像上面代码那样把输入函数的数组参数做一次拷贝。

4. 先排序然后相邻项比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var arr = [1, 2, 3, 1, 2, 3]
function unique(arr) {
var array = [...arr].sort()
var res = [array[0]]
for (var i = 1; i < array.length; i++) {
if (array[i] !== array[i - 1]) {
res.push(array[i])
}
}

console.log(res)
return res
}

unique(arr) // [1, 2, 3]

这种方法的思路是先把原数组排序,排序后的数组,如果有重复项,那么重复项之间一定是相邻的,此时再遍历数组,只需要比较相邻项,相邻项不相同就把当前项push进预先定义的结果数组中,最后返回结果数组。

这里需要注意的是要比较的相邻项应该是array[i]array[i - 1]而不是array[i]array[i + 1],因为array[i + 1]已经超出数组的length范围了。相应的,遍历的起始索引也要从1开始,而不能从0开始,因为从0开始i - 1就小于0了。还没完,起始索引从1开始,就意味着push这个动作也是从1开始的,所以要在遍历之前结果数组的初始化就应该包含数组索引0的项。