数组去重你想到几种办法呢?

前言

你是否在面试的过程中被考到过给你一个数组让你去掉重复项呢?当时你的脑海里除了用Set实现之外,你还与面试官讲了什么去重的方法呢?你能否封装来一个可复用的数组去重api呢?依稀记得当时我被问到这个问题的时候,我也没回答出很多种解决办法。那下面我来总结一下对于数组去重这道简单的面试题时,我们可以回答的方法有什么吧。

数组去重

1. 不使用数组API方法

首先我来介绍一种不是用数组身上的API的去重解法,代码如下:

var array = ['1', 1, '1', '1', '2', 2]
function unique(array) {
    let res = []
    for(let i = 0; i < array.length; i++){
        for( var j = 0; j < res.length; j++){
            if(array[i]  === res[j]){
                break;
            }
        }
        if(j === res.length){
            res.push(array[i])
        }
    }
    return res 
}
console.log(unique(array)); // [ '1', 1, '2', 2 ]

既然不使用数组自带的API方法,那我们首先考虑的就是用双重for循环了,如上述代码:

  1. 我们准备了一个空的结果数组
  2. 我们对需要去重的数组进行循环
  3. 在第一层数据中再套一层循环,根据下标判断结果数组内是否有重复项。

我们调用该方法,打印结构如上述代码的注解处,成功的实现了对数组的去重。

2. 使用 indexOf

既然有不使用数组API的,那就肯定有使用数组API的,下面看我使用indexOf完成数组的去重,代码如下:

var array = ['1', 1, '1', '1', '2', 2]
function unique(array) {
    let res = []
    for (let i = 0; i < array.length; i++) {
        if (res.indexOf(array[i]) === -1) { // 返回找到的第一个值得下标
            res.push(array[i])
        }
    }
    return res
}
console.log(unique(array))// [ '1', 1, '2', 2 ]

如上述代码, 我们巧妙了使用了indexOf查找结果数组中是否已经存在,如果不存在才向结果数组中添加,实现了数组去重。

在上述代码的基础上,我们还可以转变一下,将for循环内的语句改为

if (array.indexOf((array[i])) == array.lastIndexOf(array[i])) {
    i++
} else {
    array.splice(array.lastIndexOf(array[i]), 1)
}

不新增其他变量,直接通过indexOf和lastIndexOf判断该值是否在原数组内为唯一值,从而直接修改原数组,实现数组的去重。

3. 使用 sort

对于数组去重,我们除了通过下标找出是否有重复项之外,我们还可以先排序,然后在判断前后项是否相同来实现去重,代码如下:

var  array = [1, 3, 5, 4, 2, 1, 2, 4, 4, 4]
function unique(array) {
    let res = []
    let sortedArray = array.concat().sort() //concat() 返回新的数组
    let seen;
    for (let i = 0; i < sortedArray.length; i++) {
        if (!i || seen !== sortedArray[i]) {
            res.push(sortedArray[i])
        }
        seen = sortedArray[i]
    }
    return res
}
console.log(unique(array)); // [ 1, 2, 3, 4, 5 ]

如上述代码, 我们先获取一个排好序的新数组,再对新数组进行循环,判断保存前一个值的seen与当前值是否相同来实现数组去重。

温馨小提示: 由于数组的排序方法不能区分数组和字符串,所以想要使用此方法必须要保证数组的值的类型相同,不然会出bug

4. 使用 filter

既然都用到了sort排序了,那我直接抬出ES6数组新增的filter过滤器API也不过分吧,代码如下:

var array = ['1', 1, '1', '1', '2', 2]
function unique(array) {
    let res = array.filter((item, index, array) => {
        return array.indexOf(item) === index
    })
    return res
}
console.log(unique(array)); // [ '1', 1, '2', 2 ]

如上述代码,filter直接使用array.indexOf(item) === index作为过滤条件返回出一个新的数组,实现数组去重。

如上述代码,我们结合了 indexOf方法作为过滤条件,那我们也可以结合一下sort方法吧,直接使用一行代码就解决了数组的去重。代码如下:

function unique(array) {
    return array.concat().sort().filter((item, index, array) => !index || item !== array[item - 1])
}
console.log(unique(array)); // [ '1', 1, '2', 2 ]

5. 使用Set、Map、或者对象

除了上述的通过数组API和不使用数组API的方法外,我们还能想到的就是借助对象来实现数组的去重。使用Set数据结构是我们最容易想到的办法,使用Map与对象方法的相似,都是以数组的值作为key,再将所有的可以取出来组成一个数组。 我就不给小伙伴们演示代码了,感兴趣的小伙伴可以自己动手试试。

(对于对象的key只能为字符串这个问题,我们可以换个思路,将下标存为key,值存为value,判断不同key的值相不相同来实现数组去重。我们还可以在存key时加上其类型,然后进行一次转换。)

自己封装一个去重API

在介绍上述数组去重的方法后,我们再来总结一下,将其融合成一个有复用性,而且还可以适用不同情况的API方法。

我来介绍一下如下我封装的一个数组去重的API方法,

  1. 该方法可接受三个参数,第一个参数为需要去重的数组,第二个参数为该数组是否为排好序的数组,第三个参数为一个回调函数
  2. 该回调函数也有三个参数,分别为值,下标,需要去重数组。该回调函数的作用是方便用户对数组进行一些额外的处理(例如将大写转为小写)
  3. 第二,三参数可不传递。
var array = [1, 2, '1', 'a', 'A', 2, 1]
var array2 = [1, 1, '1', 2, 2]
function uniq(array, isSorted, iteratee) {
    let  seen = []
    let res = []
    for(let i = 0; i < array.length; i++){
        let computed = iteratee ? iteratee(array[i], i,array) : array[i]
        if(isSorted) {
            if(!i || seen !== array[i]){
                res.push(array[i])
            }
            seen = array[i]
        }else if(iteratee) {
            if(seen.indexOf(computed) === -1){
                seen.push(computed)
                res.push(computed)
            }
        }
        else {
            if(res.indexOf(array[i]) === -1) {
                res.push(array[i])
            }
        }
    }
    return res
}
let result = uniq(array, false, function(item, index, arr){
    return typeof item == 'string' ? item.toLowerCase() : item
})
console.log(result); // [ 1, 2, '1', 'a' ]
console.log(uniq(array2, true)); // [ 1, 2 ]

总结

对于数组的去重,当我们能在面试中说到这个多方法的话,这道面试题也就过了,虽然这道面试不难,但如果我们想要想到这个多方法的话,还是需要许多知识储备的。