凑热闹的JavaScript数组去重

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

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

内招:严格等价/arr#indexOf

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

//外招:双层循环 + 内招:严格等价
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 底层仍是用了严格等价。 可是对于有些值是没法作到去重的:bash

  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。ui

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

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

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非法值。code

内招: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/ */

相关文章
相关标签/搜索