js中的数组去重?小白看这篇文章就够了

426 阅读7分钟

前言

数组去重是所有语言的算法题中绕不开的一个问题,也是在很多大厂面试的时候会碰到的常考题。这是每个小白必须要打败的“怪”。那么,今天,就跟着我的脚步,我们一起来浅谈一下js中的数组去重方法。

p356677621.jpg   

方法一

双层for循环:

双层for循环是一个所谓的“笨方法”,但是也是最基础的方法,其根本逻辑是:

  • 定义了一个空数组 newArr,用来存放去重后的元素。

  • 遍历原数组 arr 的每一个元素 arr[i]

    • 对于每个元素,检查它是否已经存在于新数组 newArr 中:

      • 如果在新数组中找到与当前元素相等的值,则跳出内层循环(说明该值已存在)。
    • 如果内层循环完成后,j === newArr.length(即在新数组中未找到重复的值),则将当前元素添加到 newArr 中。

  • 最后返回 newArr,即去重后的数组。

const arr = [1, 2, 3, 4, 2, 1]

function unique(arr) {
    let newArr = []
    for (let i = 0; i < arr.length; i++) {
        //新数组是否已经具有该值
        for (var j = 0; j < newArr.length; j++) {
            if (arr[i] === newArr[j]) {
                break
            }
        }

        if (j === newArr.length) {//j长度等于新数组的长度时,则证明没有
            newArr.push(arr[i])  //若没有则添加到新数组
            //arr[i]
        }

    }
    return newArr
}

console.log(unique(arr));
}

这个方法虽然基础,但是用了双层的循环,所以它的时间复杂度是O(n^2),如果数组长度很大,效率会很低

 

方法二

for+indexOf

当我们用方法一敲出数组去重的代码后,我们会思考一个问题:有没有一个方法可以代替双层for循环方法的第二层循环呢?也就是有没有方法可以判断一个数组是否存在一个指定个元素呢?当然有!数组的indexOf()方法

数组的indexOf()方法:可返回某个指定的元素在数组中首次出现的位置(下标),若不存在这个元素就返回-1。

结合for循环,for+indexOf 的数组去重方法具体逻辑如下:

  • 定义一个空数组 newArr,用于存放去重后的元素。

  • 遍历原数组 arr 的每个元素:

    • 使用 indexOf 方法检查当前元素 arr[i] 是否已经存在于 newArr 中。

      • 如果 indexOf 返回 -1,说明该元素不在 newArr 中:

        • 将该元素添加到 newArr 中。
  • 返回去重后的新数组 newArr

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));
}

 

方法三

for+includes

方法三的底层逻辑和方法二的基本一致,只是判断一个数组是否存在一个指定个元素的方法变为了 数组的includes方法

includes()方法:用于判断数组中是否包含某个特定的元素。它返回一个布尔值:如果数组中包含该元素,返回 true;否则返回 false

加上for后的核心逻辑:

  • 定义一个空数组 newArr,用于存放不重复的元素。

  • 遍历原数组 arr 的每个元素:

    • 使用 includes 方法判断 newArr 中是否已包含当前元素:

      • 如果 newArr 中不存在该元素,则将其添加到 newArr 中。
  • 最后返回去重后的数组 newArr

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 不包含 arr[i] 时,加入 newArr
            newArr.push(arr[i]);
        }
    }
    return newArr;
}

console.log(unique(arr)); // 输出:[1, 2, 3, 4]

   

方法四

filter + sort 方法

在谈这个方法之前,我们需要了解什么是 filter()方法sort()方法

filter()方法: 用于创建一个新的数组,新数组中的元素是原数组中满足指定条件的元素。即它用于对数组进行筛选,返回所有符合条件的元素,原数组不改变。

sort() 方法:用于对数组的元素进行排序,默认按照字符编码的顺序进行升序排列。

当我们了解了 filter()方法  和 sort()方法 之后,再思考代码逻辑就很简单了,当我们把数组用 sort()方法 排好序后,只需要不断地将当前数组中的元素和上一个比较,当前元素只有不等于前一个元素时,才保留当前元素,具体代码实现如下:

  • 初始化新数组 newArr

     let newArr = [...arr];
    
    • 使用扩展运算符 ... 创建 arr 数组的一个浅拷贝,确保对 newArr 的修改不会影响原始数组 arr
  • 排序数组

    调用 sort() 方法对 newArr 数组进行排序。

      newArr.sort((a, b) => a - b);
    
  • 去重操作

    return newArr.filter((item, index, array) => {
        return index === 0 || item !== array[index - 1];
    });
    
    • 使用 filter() 方法对数组进行去重:

      • filter() 的回调函数接受三个参数:当前元素 item,当前元素的索引 index,以及原始数组 array

      • 在回调函数中:

        • index === 0:对第一个元素来说,直接返回 true,保留该元素。
        • item !== array[index - 1]:对于后续的元素,只有当当前元素不等于前一个元素时,才返回 true,即保留当前元素,达到去重的效果。
  • 返回去重后的新数组

    • 最终返回 newArr,它是去重且排序后的数组。

完整代码:

const arr = [1, 2, 3, 4, 2, 1]
function unique(arr) {
    let newArr = [...arr]; // 创建新数组
    newArr.sort((a, b) => a - b); // 按数字大小排序
    return newArr.filter((item, index, array) => {
        return index === 0 || item !== array[index - 1]; // 去重
    });
}

console.log(unique(arr);  // 输出:[1, 2, 3, 4]

 

方法五

filter + {} 方法

在这个方法里,我们首先要知道一个关于对象的小知识,那就是,对象里的key值不能重复,只能唯一!既然如此...那么一切就好办了!具体代码逻辑如下:

  • 初始化一个空对象 obj

    let obj = {};
    
    • 用于记录数组中已经出现的元素,键是数组的值,值是布尔类型 true,表示该值已存在。
  • 使用 filter() 方法过滤重复值

    return arr.filter((item, index, array) => {
        return obj[item] ? false : (obj[item] = true);
    });
    
    • 遍历数组 arr

      • filter() 的回调函数返回布尔值,决定是否保留当前元素。

      • 检查当前元素 item 是否已存在于 obj 中:

        • 如果存在(obj[item]true),返回 false,不保留该元素。
        • 如果不存在,将其添加到 obj 中,并设置为 true,同时返回 true,保留该元素。

完整代码如下

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))//输出结果[1,2,3]

 

方法六

Set 方法

Set 是一种ES6 提供了新的数据结构,用于存储唯一值。也就是说Set 中的所有值都是唯一的,不允许重复。如果向 Set 添加重复的值,则只会保留一个。

假设输入数组为 [1, 2, 3, 2, 1]

  • 创建一个 Set

    new Set(arr) // 结果为 Set {1, 2, 3}
    
    • Set 自动去掉了数组中的重复元素。
  • Set 转换为数组:

     [...new Set(arr)] // 结果为 [1, 2, 3]
    
  • 最终返回去重后的数组 [1, 2, 3]

完整代码如下:

let  arr = [1, 2, 3, 2, 1]


function unique(arr){
    return [...new Set(arr)]
}


console.log(unique(arr))

 

时间复杂度

以上六种办法各有千秋,但东西总分好坏,六种方法的时间复杂度也各有不同。

方法时间复杂度
双层 for 循环O(n²)遍历数组一次,复杂度为 O(n),双层循环复杂度为 O(n²)
for + indexOf          O(n²)       indexOf 方法本质上是一次线性查找,最坏复杂度为 O(n)
for + includesO(n²)includes 的底层实现与 indexOf 类似,也是 O(n)
filter + sortO(n log n)sort 通常为 O(n log n) ,filter 遍历一次数组,复杂度为 O(n) 整体复杂度为 O(n log n)
filter + 对象O(n)遍历数组一次,复杂度为 O(n)。对象属性的存取操作为 O(1)
SetO(n)Set 的添加和检查操作为 O(1),遍历数组一次为 O(n)

当我们碰上不同的使用场景,可以选择对应的数组去重的“最优解”。

总结

在这篇文章里,我们一起浅谈了一下js中数组去重的六种办法,看到这儿,恭喜你,你已经学会了js中的数组去重,打败了数组去这只小怪物,接下来的日子里,我们一起努力学习,继续在学习js的路上打怪升级吧!

p356851311.jpg