在JavaScript编程中,处理数组时经常会遇到数据重复的问题。数组去重作为数据处理中的一个重要环节,对于确保数据的唯一性和准确性具有至关重要的作用。无论是在开发Web应用、处理数据集合,还是进行算法实现,数组去重都是一个常见且实用的需求。本文将深入探讨JavaScript数组去重的多种方法,包括传统方法和现代方法,以及它们的优缺点和性能表现。
假设有这么一个数组
const arr =[1,2,3,4,2,1],我们需要将数组中重复的数据进行去除,那我们可以用哪些方法呢
双层for循环
const arr = [1, 2, 3, 4, 2, 1]
function unique(arr) {
let newArr = []
for (let i = 0; i < arr.length; i++) {
//新数组是否存在该值
//arr[i]
for (var j = 0; j < newArr.length; j++) {
if (arr[i] == newArr[j]) {
break;
}
}
if (j == newArr.length) {
newArr.push(arr[i])
}
}
return newArr
}
console.log(unique(arr));
这是最为朴素的方法,我们创建一个空的新数组,然后对原数组进行遍历,然后在遍历新数组,将原数组内每一个遍历得到的值与新数组的所有值进行比较,如果有相同的值,那么就跳过循环,如果没有相同的值,那么就等循环结束,在新数组遍历结束的时候判断j是否等于新数组的长度,如果不等于,那么就是跳过的循环,如果等于,那么就是循环自动结束,则往新数组中添加原数组本次循环的数据,最后在原数组遍历完成后返回我们创建的新数组,新数组就是去掉重复数据后的数组了 下面是代码的运行结果
可以看到新数组将重复的数据都去除了,但是这种写法代码量和时间复杂度都不算好,我们有没有更好用的方法呢,那当然是有的
for + indexOf
在js这门语言中,为我们打造了一个判断数组内是否具有该数据的方法indexOf,我们可以利用这个方法直接判断新数组中是否含有我们想传进去的数据
const arr = [1, 2, 3, 4, 2, 1]
function unique(arr) {
let newArr = []
for (let i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i])
}
}
return newArr
}
console.log(unique(arr));
还是和上面的方法一样,我们先创建一个新数组,然后对原数组进行遍历,但是在这里我们不用再遍历新数组去判断有没有和原数组遍历出来相等的值,而是直接用indexOf进行判断,如果判断没有,则返回-1,然后将这个新数组没有的值放入新数组中,如果有则什么都不做,等待下一次循环,这种方法是不是就节省了一些代码量,那我们只写了一次循环,时间复杂度降下去了吗,其实没有,在js中打造的这个的方法里也是进行了一次循环,所有时间复杂度还是n2。下面是代码执行结果
for + includes
js中还有一个和indexOf类似的方法includes,只不过返回的类型不同includes方法判断后返回的是true或false,我们来使用一下这种方法
const arr =[1,2,3,4,2,1]
function unique(arr){
let newArr=[]
for(let i = 0;i<arr.length;i++){
if(!newArr.includes(arr[i])){
newArr.push(arr[i])
}
}
return newArr
}
console.log(unique(arr));
如果不等返回false我们在前面加一个!变为true,效果就变成如果不等则往新数组内添加值,我们用代码结果来验证一下
filter + sort
sort是对数组进行升序排序或者降序排序arr.sort((a, b)=>{ return a - b})是升序排序,arr.sort((a, b)=>{return b - a})是降序排序,默认是升序排序,我们来看看效果
const arr =[1,2,3,4,2,1]
arr.sort((a, b) => {
return a - b
})
console.log(arr)
arr.sort((a, b) => {
return b - a
})
console.log(arr)
下面是运行结果
可以看到,这和我们预想的效果是一样的。
再来看看filter,arr.filter((item,index,arr) => {}),它接收一个箭头函数,箭头函数再filter中会触发,然后我们可以得到item(数组每一项的数据),index(数组的下标),arr(传入数组本身),看下面代码演示
const arr = [1, 2, 3, 4, 2, 1]
arr.filter((item,index,arr) => {
console.log(item,index,arr);
})
我们传入了一个数组,并在每一次遍历时将数据,下标,原数组输出一遍,我们看看执行结果
可以看到结果和我们分析的一致
此外filter有一个空间复杂度,它会创建一个数组存储满足条件的数据,看下面例子
const arr = [1, 2, 3, 4, 2, 1]
let arr2 = arr.filter((item, index, arr) => {
return item <= 2
})
console.log(arr2);
这个arr2就会被赋值成filter方法创建出来的数组,里面是满足条件的数据,看看下面代码结果
那利用这两个方法,我们可以实现数组去重
const arr =[1,2,3,4,2,1]
function unique(arr){
return [...arr].sort().filter((item,index,array)=>
!index ||item!=array[index - 1]
)
}
console.log(unique(arr));
让我们来分析这段代码,我们实现数组去重首先不能改变原数组的数据,因此我们可以用[...arr]来创建一个内容与原数组一样的新数组,然后对新数组进行升序排序,对排序后的数组使用filter方法,这个方法帮我们创建了一个新数组,然后我们将满足下标为0和数据与前一项不同的数据放入新数组当中,最后将这个新数组进行返回,我们就达到了去重这一效果,下面是代码的执行结果
用这两个方法只对数组进行了一次遍历,时间复杂度肯定是比前几种方法优秀。
filter + {}
我们还可以利用哈希表的思路,对数组进行遍历,然后往对象中存key,如果这个遍历的数据在对象中没有,那么就把这个数据当成对象的key,如果对象中有这个key,那么就不存为key,看下面代码实例
let arr = [1,2,3,1,2]
function unique(arr){
let obj ={}
return arr.filter((item,index,array)=>{
return obj[item]?false :(obj[item]=true)
})
}
console.log(unique(arr));
我们先创建一个对象,然后我们对原数组使用filter方法,在传入的箭头函数中,我们对遍历的数据进行判断如果这个数据在对象中为key,则不符合条件,我们返回false,如果这个数据不在对象中为key,那么我们返回这个item,并在对象中把这个值存为key,最后将满足在对象中不为key的数据存入filter为我们创建的方法中,下面是代码的执行效果
这种方法代码简洁,且时间赋值度得到了优化,比较推荐。
Set
Set是一种数据结构,利用这种数据类型,我们可以写出最优雅简洁的代码来达到去重这个操作
let s = new Set()
s.add(1)
s.add(2)
s.add(3)
s.add(1)
console.log(s);
这段代码我们往用Set创建出来的对象中添加四个值,其中1,为重复项,那我们来看看添加数据后的对象s长什么样子
可以看到,在
s对象中,重复项被自动消除了,那我们直接传入一个数组试试
let s = new Set([1,2,3,1,2])
console.log(s);
看看效果
可以看到数组中的数据直接就被去重了,但是我们要的是一个数组,所以我们需要将对象进行结构再变成数组
看看这个对象是否能结构呢
let s = new Set([1,2,3,1,2])
console.log(...s);
可以看到是没有问题的,那让我们来打造方法
let arr = [1, 2, 3, 1, 2]
function unique(arr) {
return [...new Set(arr)]
}
console.log(unique(arr));
仅仅一行代码便实现了这个效果,相当的优雅简洁。
结语
本文深入探讨了JavaScript数组去重的多种方法,从传统的双层for循环到现代的Set数据结构,每种方法都有其独特的优缺点和适用场景。在实际开发中,我们可以根据具体需求选择合适的方法来实现数组去重。随着JavaScript的发展,现代方法如Set和哈希表思路的filter+{}不仅代码简洁,而且性能优越,逐渐成为数组去重的首选方案。希望本文能够帮助读者更好地理解和应用数组去重技术,提升编程效率和代码质量。