Javascript數組去重方法彙總

前言

關於數組去重,也算是一個比較常見的面試題了。但是有點開發經驗的同學又會發現,前端數組去重的操作很少見(也可能是我個人經驗不足遇到的比較少)。這是爲什麼呢?。我感覺可能是大部分的去重操作被後端處理了。個人見解!
雖然,前端很少用到。本着愛(kuai)與(su)和(zhang)平(xin)的精神,我們也來學習一波!

方法彙總

一、IndexOf

IndexOf是數組的一個原生方法,當你傳入一個參數的時候,它會返回入參數的索引值。如果未找到就返回 -1。利用這一特性,

function unique(array) {
	var res = []
	for(var i = 0; i < array.length; i++) {
		if(res.indexOf(array[i]) === -1) {
			res.push(array[i])
		}
	}
   return res
}

相信一般求職者都能回答出這個,當初我在面試的時候,也回答了這一項。也只是回答出了這一項。後來面試官繼續問:還有其他方式嗎?這種方式的效率高嗎?複雜度是多少?
在這裏插入圖片描述

後來這場面試就失敗了,沒關係!痛定思痛繼續學習。
後來我知道了,indexOf需要把每個參數重新判斷一遍。效率很低下。而且indexOf還不能查到NaN的索引值,如下代碼:

var arr = [1, NaN]
arr.indexOf(NaN)   // -1

那如何解決這個查詢問題呢?我們下次談include和indexOf的時候細說!

我們先來驗證一下indexOf的效率問題?後面想想怎麼提高?

我們先準備兩個數組,一個長度爲:100000 一個:50000

var arr1 = Array.from(new Array(100000), (x, index)=>{
  return index
})

var arr2 = Array.from(new Array(50000), (x, index)=>{
  return index+index
})

var handleArray = arr1.concat(arr2)
console.log('原數組的長度',handleArray.length)
let start = new Date().getTime()
console.log('開始數組去重...')
function unique(array) {
  var res = []
  for(var i = 0; i < array.length; i++) {
    if(res.indexOf(array[i]) === -1) {
      res.push(array[i])
    }
  }
  return res
}
console.log('去重後的長度', unique(handleArray).length)

let end = new Date().getTime()
console.log('耗時', end - start)

執行結果如下:
在這裏插入圖片描述
一個150000的數組,去重要7723毫秒。(該值存在波動,這是一個平均值,後續相同)

二、排序後去重(sort())

我們可以先對數組進行排序,將相似的值排到一起

function unique(array) {
  var sortArr = array.concat().sort() //返回新數組
  var res = [sortArr[0]]
  for(var i=1; i < sortArr.length; i++) {
  	if(sortArr[i] !== sortArr[i-1]) {
  		res.push(sortArr[i])
  	}
  }
  return res
}

代碼解析:

  • 1、我們先對數組進行排序,爲了不影響原數組,我們使用了concat()方法,返回新數組。
  • 2、將排序好的數組進行相鄰位的比較。如果不相等,推到結果數組裏。相等跳過操作。
  • 3、res = [sortArr[0]] 和 i = 1 是個初始化操作

這樣的做法,省去了對重複參數的索引。效率上面確實也提升不少。對於一些重複性比較高的數組進行去重,效率明顯提升。

我們看一下優化後的效果:
在這裏插入圖片描述
看到這個結果,嚇我一跳啊!竟然只要106毫秒。優化真的是一門學問啊!

三、利用雙層循環

上面的兩種方法都是使用了indexOf這個api,除了這個有沒有其他的呢?答案是肯定的。就是利用雙層循環

function unique(array){
	var res = []
	for(var i=0;i<array.length;i++) {
	  	for(var j=0;j<res.length;j++) {
	  		if(array[i] === res[j]) {
	  			break
	  		}
	  	}
	  	if(j === res.length) {
	  		res.push(array[i])
	  	}
	}
	return res	
}

代碼解析:

  • 1、對初始值數組進行每一項與目標數組每一項進行比較
  • 2、如果有重複就跳出循環
  • 3、如果全部都不相等,那麼j就會等於res.length。此時,把值添加到res中

這樣的方法,不失爲一種新解法。但是由於雙層循環的存在,其效率也不會改善多少。
在這裏插入圖片描述
我們看到相比方法一確實快了不少,但是效率還是比較低下的。不推薦大家使用!

四、利用ES6 new Set()

這個是es6的語法糖,極度簡單明瞭!效率也是槓槓滴!

function unique(array){
	return Array.from(new Set(array) 
	// return [...new Set(array)]
}

這樣的方法,沒啥好說的。就是一個字快!

我們看看執行效果:
在這裏插入圖片描述
官方的優化總是不讓人失望,真的太牛了!爸爸始終是爸爸!
在這裏插入圖片描述

五、利用對象屬性不可重複(該方法有侷限性,不推薦使用)

function unique(array){
  var obj = {}
  var res = []
  for(var i =0; i < array.length; i++) {
  	if(!obj[array[i]]) {
  		res.push(array[i])
  		obj[array[i]] = 1
  	} else {
  		obj[array[i]]++
  	}
  }
  return res
}

代碼解析:

  • 1、申明一個對象,將數組裏每一項的值作爲對象的key
  • 2、判斷obj.key值是否爲undefined?是!則目標數組將key作爲一個參數項添加進來。並給obj.key 一個默認值 1
  • 3、obj.key不是undefined的話,則證明目標數組已經有key的參數項。對obj.key進行加1操作即可。

再來看一下:
在這裏插入圖片描述
看到這個圖的時候!我不禁多執行了幾次。但是事實告訴我:你沒有看錯,比new Set()還要高效。

重點說明一下:就是對象 1 和 ‘1’ 這樣的方式無法區分。需要引入typeof hasOwnProperty等操作。這裏就不展開討論了。

總結

在寫去重之前,我也看了網上不少博文。說多少種的都有。其實感覺就是使用了不同的語法糖進行了循環操作和刪除操作。比如 filter reduce for of include splice
Map等方法。思路和想法確實很新穎。我把鏈接放到了下面。還有我自己的測試代碼,如需自取。

正在讀此文的朋友,如果明天面試官問你這樣的問題,你是不是跟我一樣的心態呢?
在這裏插入圖片描述

源碼地址:

源碼
我的小站: https://shenzhiyong.com.cn

參考鏈接:

JavaScript數組去重(12種方法,史上最全)

JavaScript 高性能數組去重
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章