JavaScript数组去重讲解详细
前言
博主也是刚刚开始水面试题才去了解JavaScript,并没有系统地去学习过JavaScript,如果博客或者其他博文有哪里不正确,不足的地方,欢迎各位指正、补充。话不多说,其实方法都一样(我就是加以自己的理解,也不知道对不对)例子均来自牛客面试题。
1. 利用ES6 Set去重(ES6中最常用)
1 | function unique (arr) { |
解释
这是使用了ES6中新增的
Array.from(
)和Set()
来进行去重。
- 定义了一个函数
unique
,该函数接受一个数组作为参数。- 使用
Set
数据结构来去重,将数组转为Set结构,再将Set结构转回数组。- 返回去重后的数组。
Array.from()
方法用于将类数组对象( array-like object )
和可遍历对象( iterable object )
转化为一个新的数组实例。Set()
是ES6新增的数据结构,用于存储唯一的值,不允许重复。它类似于数组,但是成员的值都是唯一的。因此,首先将输入的数组转化为一个
Set
数据结构(利用Set可以去除重复项),然后再将Set
数据结构转化为数组返回给用户。在这个例子中,输入的数组
arr
包含了不同数据类型的元素,包括数字、字符串、布尔值、undefined、null、以及对象等。其中NaN存在特别的问题,Set()
中的NaN值
可以去重,但是两个NaN之间不相等,因此去重的结果中只会保留一个NaN。不考虑兼容性,这种去重的方法代码最少。这种方法还无法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法。
2. 利用for嵌套for,然后splice去重(ES5中最常用)
1 | function unique(arr){ |
解释
采用双重循环和splice方法,双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
- 定义了一个函数
unique
,该函数接受一个数组作为参数。- 使用双重循环来遍历数组中的每一个元素,并找到数组中的重复元素。
- 如果找到数组中的两个元素相同,那么就使用
splice
方法删除第二个元素,并将内层循环的计数器j减1,从而保证循环安全、完整。- 返回去重后的数组。
在循环操作中,对于每一个元素,它会从它后面的元素开始逐个比较,如果发现有相同的元素,则使用splice方法将后面的元素删除,并且j自减1。因为splice方法的调用,这个嵌套的循环可能需要执行许多次,性能上会比Set方法慢一些。
然后函数返回去重后的数组。但是这个方法存在一些问题,比如:
- NaN与对象一样,被认为是不同的元素类型,因此在此方法中不能移除掉数组中的所有NaN。
- 原数组中有两个null,但是在去重后,这两个null却被消失了,这是因为
splice
方法在使用时,可能会引起数组索引的变更,会影响当前的循环操作。- 数组中的元素类型过于多样化时,并不容易保证去重的结果正确,比如在此例子中,两个{}对象并没有被移除,因为两个空对象之间是无法被“==”比较的。
3. 利用indexOf去重
1 | function unique(arr) { |
解释
- 定义了一个函数
unique
,该函数接受一个数组作为参数。- 检测传入的参数是否为数组,如果不是则打印出’type error’的错误信息,并结束函数的执行。
- 定义了一个变量
array
,用于存储去重后的数组。- 使用for循环遍历传入的数组,取出数组中的每一个元素。
- 如果
array
数组中不存在该元素,则将该元素加入array数组中。- 返回去重后的
array
数组。然后函数返回去重后的数组。但是这个方法存在一些问题,比如:
- 与第二种方法一样,这个方法也无法正确地去重NaN和{}对象。
- 该方法的执行效率不如第一个方法Fast Set(ES6新增的Set)和第二个方法while+hasOwnProperty实现的快速去重。
4. 利用sort()
1 | function unique(arr) { |
解释
- 定义了一个函数
unique
,该函数接受一个数组作为参数。- 检测传入的参数是否为数组,如果不是则打印出’type error’的错误信息,并结束函数的执行。
- 对传入的数组进行排序。
- 定义了两个变量
arrry
和obj
,分别用于存储去重后的数组和存储数组元素出现次数的对象。- 将排序后的数组的第一个元素加入到
arrry
数组中。- 使用for循环遍历排序后的传入的数组,取出数组中的每一个元素。
- 如果
arr[i]
和arr[i-1]
的值不相等,那么就将该元素加入到arrry
数组中,实现去重。- 最后返回去重后的数组
arrry
。然后函数返回去重后的数组。但是这个方法存在一些问题,比如:
- 与前面三种方法一样,这个方法无法正确地去重包括NaN和{}对象在内的某些数据类型。在此例子中,NaN和{}对象没有被正确去重,而且它们还变得更多了(排序后相邻的NaN变成了3个,{}对象也变成了2个)。
- 类型不同的数据进行排序后,得到的可能并不是想要的顺序,比如在此例子中,数字0出现在了末尾。
5. 利用对象的属性不能相同的特点进行去重(这种数组去重的方法有问题,不建议用,有待改进)
1 | function unique(arr) { |
解释
- 检测传入的参数是否为数组,如果不是则打印出’type error’的错误信息,并结束函数的执行。
- 定义了两个变量
arrry
和obj
,分别用于存储去重后的数组和存储数组元素出现次数的对象。- 使用for循环遍历传入的数组,取出数组中的每一个元素。
- 如果
obj
对象中未出现这个元素,即该元素没有出现过,就将它加入到arrry
数组中,并给相应的obj
属性赋初值1表示出现了一次。- 如果
obj
对象中已经出现了该元素,那么就只将相应obj
的属性值加1,表示该元素又出现了一次。- 最后返回去重后的数组
arrry
。然后函数返回去重后的元素数组。但是这个方法也存在一些问题,比如:
- 与前面所有方法一样,这个方法也无法正确地去重包括NaN和{}对象在内的某些数据类型。但是在此例子中,这个方法可以正确地去除所有重复元素。
- 此方法可能会改变数组中元素的顺序,因为在循环遍历数组时,对象的属性遍历顺序并不是按照数组元素的出现顺序,因此去重结果有可能不是原数组中元素的原始顺序。
6. 利用includes
1 | function unique(arr) { |
解释
- 创建一个函数
unique
并传入一个参数arr
,这个参数arr
是需要去重的数组。- 在函数内部,先检查传入的参数是否为数组,如果不是则打印一条类型错误的消息,并退出函数;否则继续下一步。
- 创建一个新的空数组
array
,用于存储去重后的结果。- 使用 for 循环遍历原始数组
arr
,在遍历过程中对每个元素进行判断,如果array
中不包含该元素,就将其添加到array
数组中。- 遍历完成后,返回
array
数组,其中包含了原始数组中的所有不重复元素。- 注意,由于
includes
方法无法去重对象类型,所以在最终的结果中仍会保留数组中出现的对象值。然后函数返回去重后的元素数组。但是这个方法也存在一些问题,比如:
- 与前面所有方法一样,这个方法也无法正确地去重包括{}对象在内的某些数据类型。在此例子中,两个空对象没有被正确去除。
- includes方法ES7新增,不是所有浏览器均支持这个方法。
7. 利用hasOwnProperty
1 | function unique(arr) { |
解释
- 创建一个函数
unique
并传入一个参数arr
,这个参数arr
是需要去重的数组。- 创建一个空对象
obj
。- 使用
filter
函数来遍历数组。filter
函数对数组中所有元素执行给定的函数,返回一个新数组,该数组中的所有元素都是在测试函数中返回 true 的原始数组元素。- 在
filter
函数中,使用匿名函数来检查当前元素是否已经在原数组中出现。为了避免比较数据类型,使用 typeof 操作符获取其类型,并将其与元素值结合为一个字符串typeof item + item
。- 在每次遍历过程中,使用
hasOwnProperty
检查对象obj
中是否已经存在当前元素。如果存在,则返回 false,不加入到结果数组中。- 如果不存在,则将当前元素添加到
obj
对象中,并返回 true,将其加入到新数组中。- 遍历完成后,返回已经去重的数组。
8. 利用filter
1 | function unique(arr) { |
解释
- 创建一个函数
unique
并传入一个参数arr
,这个参数arr
是需要去重的数组。- 使用
filter
函数来遍历数组。filter
函数对数组中所有元素执行给定的函数,返回一个新数组,该数组中的所有元素都是在测试函数中返回 true 的原始数组元素。- 在
filter
函数中,使用匿名函数来检查当前元素是否已经在原数组中出现。使用indexOf
方法查找当前元素在原始数组中的第一个索引,如果索引值与当前索引值相等,即表示当前元素是在原始数组中第一次出现,将其添加到结果数组中。- 如果当前索引不是第一次出现,即与原始数组中其他索引值相等,则不添加到结果数组中。
- 遍历完成后,返回已经去重的数组。
- 此方法在遍历数组的过程中会多次调用
indexOf
方法,又由于indexOf
方法的时间复杂度为 O(n),因此其效率可能不够高。- 该方法可能不适用于处理元素为对象的数组,因为对象的引用地址不同,虽然对象具有相同的类型和值,但是
indexOf
方法不会被认为两个不同的对象是相同的元素,因此无法对它们去重。
9. 利用递归去重
1 | function unique(arr) { |
解释
- 创建一个函数
unique
并传入一个参数arr
,这个参数arr
是需要去重的数组。- 创建一个新的变量
array
并将其值设置为传入参数的值。- 创建一个变量
len
并将其赋值为array
数组的长度。- 使用
sort()
函数对array
数组进行排序。这么做是为了更方便地进行数组去重。- 函数中创建了一个名为
loop
的内部函数。loop
函数使用递归的方法遍历数组,比较前后两个元素是否相等,如果相等,则将后一个元素从数组中删除。- 调用
loop(len-1)
函数,开始遍历array
数组。- 函数返回
array
数组(已经去除了重复元素)。
10. 利用Map数据结构去重
1 | function arrayNonRepeatfy(arr) { |
解释
- 创建一个函数
arrayNonRepeatfy
并传入一个参数arr
,这个参数arr
是需要去重的数组。- 创建一个新的 Map 对象
map
,用于存储数组中的值。- 创建一个新的数组
array
,用于存储去重后的结果。- 使用 for 循环遍历数组
arr
。在遍历过程中,获取当前元素的值。使用 Map 的 has() 方法检查该元素是否已经存在于 Map 中。- 如果存在,将该元素对应的 Map 的 value 值设为 true。
- 如果不存在,则将该元素加入到数组
array
中,并将 Map 的 value 值设为 false。- 遍历完成后,返回已经去重的数组
array
。
11. 利用reduce+includes
1 | function unique(arr){ |
解释
- 创建一个函数
unique
并传入一个参数arr
,这个参数arr
是需要去重的数组。- 使用
reduce
函数来遍历数组。reduce
函数将数组元素传递给指定的函数,返回一个输出结果。初始值为空数组[]。- 在
reduce
函数中,使用箭头函数处理每个元素。如果这个元素没有在初始值数组prev
中出现过,那么就将这个元素放入数组prev
中,使用逗号运算符返回新数组[…prev,cur]。- 如果元素已经出现在
prev
数组中,那么就返回之前的prev
数组,从而达到去重的目的,即 return prev。- 遍历完成后,返回已经去重的数组
prev
。
12. […new Set(arr)]
1 | [...new Set(arr)] |
解释
- 在 ES6 中,Set 是一种新的数据结构,表示一组无序且唯一的值。Set 可以接收一个可迭代对象,比如数组,在迭代对象中进行去重操作。
- 将数组
arr
传递给 Set 构造函数,它会自动去除数组中的重复项。- 使用扩展运算符
[...]
将 Set 转换成数组,从而实现去重。- 返回已经去重的数组。