简单的数组去重引发的一些问题

393 阅读2分钟

当前业务需求需要判断一个数组中是否有重复的数字然后在进行操作,于是想起了用对象的key值唯一性来进行判断, 然后深入发现会产出一堆的问题如下

  1. 如果数组中包含对象怎么办?
  2. 使用JSON.stringify会出现的问题
  3. 如何最优方法去解决去重问题

1. 利用key值来判断是否重复

  function uniqueArr(arr) {
        let temp = {},
            resArr = []
        arr.forEach(item => {
            if (!temp[item]) {
                // 挂到对象上给下次判断使用 需要转成字符串避免null undefined等值
                temp[item] = item + ''
                resArr.push(item)
            }
        })
        return resArr
    }
    
    let arr = [1, 1, 2, 3, null, null, undefined, undefined]
    uniqueArr(arr) // [1, 2, 3, null, undefined]

问题1: 如果数组里面是一个个对象

let arr = [{ a: 1 }, {b: 1}]
function uniqueArr(arr) // [{a: 1}]

因为任何对象直接作为属性挂在到其他对象上会成被toString()成为 [object Object],所以两个对象都相等结果只会输出第一个对象{a:1}

解决办法: JSON.stringify() 将对象转为字符串然后进行判断

function uniqueArr(arr) {
        let temp = {},
            resArr = []
        arr.forEach(item => {
            // 将对象转为字符串后进行挂载判断
            if (!temp[JSON.stringify(item)]) {
                temp[item] = item + ''
                resArr.push(item)
            }
        })
        return resArr
    }
let arr = [{ a: 1 }, {b: 1}]
uniqueArr(arr)  // [{ a: 1 }, {b: 1}]

问题2: 此时看似解决的了对象问题但是还有很多坑在里面, 例如 [{a:1}, {a:1}] 由于是两个对象在不同地址两个应该是不同值(如: {} === {} 返回false),但是使用JSON.stringify后会将它们看为重复的值, 并且JSON.stringify并不是万能的它对正则类型、undefiend类型转化的时候有副作用,之后文章再会讨论这个问题.

所以最优解是使用es6中的Set对象, 既简单又高效

2. set方法

先看看Set的MDN简单介绍:

Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

元素是唯一的则可以利用它的这一特性来解决重复问题

let arr = [{ a: 1 },{ a: 1 }, {b: 1}]
  
function uniqueArr(arr) {
    let set = new Set(arr)
    return [...set] // 展开set中的数据
}

console.log(uniqueArr(arr));  [{ a: 1 }, { a: 1 }, {b: 1}]

更加简单的精简的写法 const allUnique = arr => arr.length === new Set(arr).size;

总结:

去重也看需求,如果简单的数字去重都可以使用,如果涉及到了对象和一些特殊类型还是优先选择Set来进行去重吧,既简单又高效~