湊熱鬧的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/ */

相關文章
相關標籤/搜索