深入了解「JS数组去重」

465 阅读3分钟

Set 方法

MDN 链接

对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用

new Set([1, 2, 3, 4, 5])  // [1, 2, 3, 4, 5]
// 它会自动去重 
// 唯一值
new Set([1, 2, 3, 4, 5, 5, 5, 5])  // [1, 2, 3, 4, 5]
// 原始值
new Set([1, 2, '1', '1', null, null ,undefined, undefined]) // [1, 2, '1', null, undefined]
// 对象引用
var a = {} 
new Set([a , a, a])  // [a]

// 它的问题是 不同的对象就不会去重
var a = {}
var b = {}
// 它就不会去重
new Set([a , b])  // [a, b]

new Set(array)去重后得到的不是数组,于是我们通常都会加上 Array.from(new Set(array)) 另一种写法是 [...new Set(array)]这个会尝试这个东西变成数组, ...表示解构然后放到数组里

对象的下标去重

var a = [???] // 里面的值不知道

function uniq(array){ ??? } // 里面内容不知道

//要求
uniq(a) // 把 a 传到你的 uniq 得到一个新数组,并且新的数组保证没有重复的元素 

举例

对它进行去重 var a = [ 1, 2, 3, 3, 4, 4 ]

  1. 首先写一个函数,这个函数有一个返回值,返回新数组
function uniq(array){
  var result = []
  return result
}
  1. 这个的要求是把 a 传给到我这个函数里,这个函数对它进行数组去重
function uniq(array){
  var result = []
  
  var hash = {}  // 新建一个空对象
  
  for(let i = 0; i < array.length; i++){  // 遍历这个数组
  // 我发现原数组里有 1 我 hash 的值就是 true ,发现原数组有啥我hash的值就是 true
  // 在 hash 这个对象里,原数组的值被当作下标了,所以重复的部分就会被去掉
  hash.push[array[i]] = true   
  }
  // 接下来我就要把它变成数组弄出来
  for(let key in hash){ // for...in 获取当前对象的所有键
    result.push(key)
  }
 
  return result
}
console.log(uniq(a)) // 打印出  ['1', '2', '3', '4']

缺点

  1. 问题1
  • 数组里的值数字变成字符串了
  1. 问题2
  • 假设原数组中间不是数字呢?比如这种: [1, 2, 3, 4, '5', 5]

    如何区分? 它会打印出 ['1', '2', '3', '4', '5']

    没有办法区分它是数字还是字符串

    举例:

     var hash = {}
     hash[4] = '数字'  // 打印出{4: '数字'}
     hash['4'] = '字符串' //有两个值吗? 不,打印的结果是 {4: '字符串'}
    
  1. 问题3
  • 如果我里面有一个对象 比如 [1, 2, 3, 4, {name: 'object'}, 5]

    我是无法通过下标去重的,它打印出 [1, 2, 3, 4, "[object Object]", 5]

    因为这个方法的下标只支持字符串,当我以这个对象为下标的时候它会变成?

     var hash = {}
     hash[{name: 'object'}] = true  // 打印出  {[object Object]: true}
    

    为啥下标是[object Object] 因为所有对象变成字符串都是一个类似这种的字符串

    var a = {}
    a.toString() // [object Object]
    

    当看到这个对象的下标是对象的时候就会调用这个对象的 toString()方法,于是就变成了上面的样子了

总结

用对象的下标去重的最大问题是: 只能统计字符串(无法区分数字和字符串),无法统计对象

面试问到了该如何答?

首先我们应该看需求能不能用 ES6 的语法如果不能用 ES6 语法,我会选择一种不太完美的算法,这种算法是这样的

  • 首先我们去遍历这个数组,然后把数组的值作为这个对象的下标储存起来,每遇到一个值就储存起来,然后如果发现是同一个值呢,就不用管它,如果是新的值就把这个值设为true,把这个 true 遍历一下放到新的数组里面 return 出来,就得到了新的数组,这是一种不太完美的数组去重方法,为何说它不太完美,第一个:它无法区分数字和字符串,第二个:如果这个数组里面有一个对象我们的去重方法就失败了,原因是我们的这个对象只支持字符串的下标,如果可以使用 ES6 的 Set 那就简单了,我们只需要 new Set(array), 然后得到的值 用Array.from()得到数组。