凑热闹的JavaScript数组去重

在以前有次面试的时候,当场敲代码就出了数组去重。当时正好看到 Set ,直接转 Set 再转 Array出了结果。然后面试官又问了问 Set 背后是做了什么工作,能不能手动循环做一下。惭愧的是经提醒,才写出了拿一个辅助Array去记录不重复的数据。方法单一,也没有涉及特殊的基本类型值和复杂的引用者。 这里做一个重新的梳理,数组去重分两步:

  1. 循环遍历数组,这是外招
  2. 比较值是否相等,这是内招

内招:严格等价/arr#indexOf

这第一式:双层循环+严格等价

//外招:双层循环 + 内招:严格等价
function deduplicate(arr){
  //记录唯一数据
  var help_arr = [];
  
  for(var i=0;i<arr.length;i++){
    for(var j=0;j<help_arr.length;j++){
      if(arr[i]===help_arr[j]){
        break;
      }
    }
    
    //能走到这里说明没有满足严格等价
    if(j===help_arr.length){
     help_arr.push(arr[i]); 
    }
  }
  
  return help_arr;
}
复制代码

这第二式:单层循环+arr#indexOf

function deduplicate(arr){
  //记录唯一数据
  var help_arr = [];
  
  for(var i=0;i<arr.length;i++){
    //如果没有找到
    if(!~help_arr.indexOf(arr[i])){
      help_arr.push(arr[i]);
    }
  }
  
  return help_arr;
}
复制代码

这第x式:arr#filter+arr#indexOf

function deduplicate(arr){
  return   arr.filter((element,index,array)=>array.indexOf(element)===index);
}
复制代码

这一式使用了 arr#filter 大大简化了代码。

其实前面这三式内招都是一样的,arr#indexOf 底层还是用了严格等价。 但是对于有些值是无法做到去重的:

  1. NaN:因为 NaN === NaN 的结果是 false
  2. object/array/regx:因为它们都是对象,引用地址不同

内招:Number.isNaN(..)/Object.is(..,..)

function deduplicate(arr){
  let countNaN = 0;
  return arr.filter((element,index,array)=>{
    if(countNaN===0&&Number.isNaN(element)){
      countNaN++;
      return true;
    }else{
     return array.indexOf(element)===index; 
    }
  });
}
复制代码

使用 Number.isNaN或者Object.is对值NaN做特殊处理。

内招:Set

function deduplicate(arr){
  return [...new Set(arr)];
}
复制代码

嗯...开挂一样,这也是我当时面试的时候写出来最快的...使用数据结构Set达到的效果也是能处理NaN。

内招:Number.isNaN(..)/Object.is(..,..)+JSON.stringify+JSON.parse

JSON.stringify + JSON.parse 能帮助部分对象去重,说是部分是因为有JSON字符串非安全值:undefined、function、(ES6+)symbol、和带有循环引用的 object,如果对象包含这些值特殊情况就会增多。

function deduplicate(arr){
  
  //引用值做JSON字符串化
  const objTransArr = arr.map(e=>{
    if(typeof e === "object" && e){
      return JSON.stringify(e);
    }else{
      return e;
    }
  })
  
  let countNaN = 0;
  return arr.filter((element,index,array)=>{
    if(countNaN===0&&Number.isNaN(element)){
      countNaN++;
      return true;
    }else if(typeof element === "object" && element){
      return objTransArr.indexOf(JSON.stringify(element))===index;
    }else{
     return array.indexOf(element)===index; 
    }
  });
}
复制代码

这里因为只想对数组内的引用数据做JSON字符串化,所以引入了辅助数组 objTransAff,它是能解决 NaN 和 object/array/regx 的去重,只要引用数据类型内不涉及JSON非法值。

内招:Object键值对+JSON.stringify

function deduplicate(arr){
  let obj = {};
  
  return arr.filter((v,i,array)=>{
    const k = typeof v + JSON.stringify(v);
    return obj.hasOwnProperty(k)?false:(obj[k]=true);
  })
}
复制代码

这一招其实挺妙的,通过 typeof v + JSON.stringify(v) 转化为对象的key,这里也解决了NaN和引用数据类型的问题。

小结

到最后其实也没有拿出一个沉重的解决所有去重的方案,但是走到这一步个人认为已经是足够了,因为实际情况下的数组去重并不需要走这么远,现在列出各招数的缺陷,具体情况具体应用就行。

严格等价/arr#indexOf缺陷:NaN、引用数据
严格等价+Number.isNaN(..)/Object.is(..,..)缺陷:引用数据
Set缺陷:引用数据
Number.isNaN(..)/Object.is(..,..)+JSON.stringify+JSON.parse缺陷:含JSON非法值的引用数据
Object键值对+JSON.stringify缺陷:含JSON非法值的引用数据

按能力从小到大的推荐方法是:

  • 严格等价/arr#indexOf 第x式:filter
  • Set
  • Object键值对+JSON.stringify

参考链接: JavaScript专题之数组去重:这篇写得超级好!

YKDJS-类型与转换-强制转换

/*封面图是盗来的,原作者在这:https://kaminario.com/company/blog/data-dedupe-all-flash-arrays/ */

转载于:https://juejin.im/post/5c8068f7e51d454dde30b3db

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值