前言
我們先來看下面的例子嚼贡,當(dāng)然來源與網(wǎng)絡(luò)捉片,地址《刪除數(shù)組中多個(gè)不連續(xù)的數(shù)組元素的正確姿勢(shì)》
我們現(xiàn)在將數(shù)組中所有的‘ a’ 元素刪除:
var arr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];
arr.forEach(function(value, index) {
value === 'a' ? arr.splice(index, 1) : '';
})
console.log(arr);
//["a", "b", "c", "d", "a", "e", "g", "f"]
只要相鄰的‘ a’ 元素秩霍, 都沒被刪除齐婴, splice不但可以刪除元素本身茄厘, 還同時(shí)可以減少數(shù)組長度( 就是抹去一切痕跡)矮冬,
這樣導(dǎo)致后續(xù)的數(shù)組元素會(huì)代替已經(jīng)刪除的元素的位置, 但是循環(huán)是按照數(shù)組的下標(biāo)按順序刪除次哈, 這樣就會(huì)漏掉遷移的元素胎署。
看到網(wǎng)上有網(wǎng)友在說使用delete進(jìn)行操作,如下:
vararr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];
arr.forEach(function(value, index) {
? value === 'a' ?deletearr[index] : '';
})
console.log(arr); //[2: "b", 3: "c", 4: "d", 7: "e", 8: "g", 10: "f"]
但是得到的arr其實(shí)是一個(gè)非常規(guī)的數(shù)組了窑滞,也就是說其實(shí)delete主要是用于對(duì)對(duì)象屬性的操作琼牧。這確實(shí)要根據(jù)自己的需求來了。
當(dāng)然簡(jiǎn)單的實(shí)現(xiàn)如下:
vararr = ['a', 'a', 'b', 'c', 'd', 'a', 'a', 'e', 'g', 'a', 'f'];varnewArr = arr.filter(function(key) {
? ? returnkey !== 'a'})
console.log(newArr); //["b", "c", "d", "e", "g", "f"]
?下面總結(jié)下常用實(shí)現(xiàn)方式哀卫。
性能測(cè)試模板
let arr1 = Array.from(newArray(100000),(item,index) => {
? ? return index;
})
let arr2 = Array.from(newArray(50000),(item,index) => {
? ? returnindex + index;
})function distinct(a,b) {
? ? // 數(shù)組去重}
console.time('去重計(jì)算時(shí)間');
console.log('去重后的長度', distinct(arr1, arr2).length);
console.timeEnd('去重計(jì)算時(shí)間');
上面創(chuàng)建了兩個(gè)數(shù)組長度巨坊,1個(gè)10W,1個(gè)5W長度的數(shù)組此改,合并到一個(gè)數(shù)組趾撵,然后進(jìn)行去重,驗(yàn)證下去重的計(jì)算時(shí)間是多長共啃。
方法一:Array.filter() + indexOf
function distinct(a,b) {
? ? let arr = a.concat(b);
? ? returnarr.filter((item,index) => {
? ? ? ? returnarr.indexOf(item) === index;
? ? })
}
思路就是ES6 中的 Array.filter() 遍歷數(shù)組占调,并結(jié)合 indexOf 來排除重復(fù)項(xiàng)∫萍簦看下結(jié)果:
從截圖可以看出究珊,計(jì)算時(shí)間花費(fèi)了19753ms的時(shí)間。
方法二:使用 for...of + includes()
function distinct(a,b) {
? ? let arr = a.concat(b)
? ? ? ? let result = []
? ? ? ? for (let i of arr) {
? ? ? ? ? ? !result.includes(i) && result.push(i)
? ? ? ? }
? ? ? ? return result
}
這種方式跟Array.filter() + indexOf實(shí)現(xiàn)思路差不多挂滓,結(jié)果如圖所示:
從圖上可以看出苦银,計(jì)算的時(shí)間跟Array.filter() + indexOf的時(shí)間差不多啸胧。
方法三:雙重 for 循環(huán)
function distinct(a, b) {
? ? let arr = a.concat(b);
? ? for(let i=0, len=arr.length; i
? ? ? ? for(let j=i+1; j
? ? ? ? ? ? if(arr[i] == arr[j]) {
? ? ? ? ? ? ? ? arr.splice(j, 1);
? ? ? ? ? ? ? ? // splice 會(huì)改變數(shù)組長度,所以要將數(shù)組長度 len 和下標(biāo) j 減一len--;
? ? ? ? ? ? ? ? j--;
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? return arr
}
優(yōu)點(diǎn):簡(jiǎn)單易懂
缺點(diǎn):占用內(nèi)存高幔虏,速度慢
看下結(jié)果:
方法四:Array.sort()
首先使用 sort() 將數(shù)組進(jìn)行排序
然后比較相鄰元素是否相等纺念,從而排除重復(fù)項(xiàng)。
function distinct(a, b) {
? ? let arr = a.concat(b)
? ? arr.sort(function(a,b){//對(duì)數(shù)組進(jìn)行排序才能方便比較returna - b;
? ? })
? ? let result = [arr[0]]
? ? for(let i=1, len=arr.length; i
? ? ? ? arr[i] !== arr[i-1] && result.push(arr[i])
? ? }
? ? return result
}
從上面代碼可以看出來想括,做了一次排序和一次循環(huán)陷谱,所以效率比前面都高,結(jié)果如圖所示:
可以看出瑟蜈,結(jié)果只花了255ms左右時(shí)間烟逊。
方法五:for...of + Object
利用對(duì)象的屬性不能相同的特點(diǎn)進(jìn)行去重,代碼如下:
function distinct(a, b) {
? ? let arr = a.concat(b)
? ? let result = []
? ? let obj = {}
? ? for (let i of arr) {
? ? ? ? if(!obj[i]) {
? ? ? ? ? ? result.push(i)
? ? ? ? ? ? obj[i] = 1? ? ? ? }
? ? }
? ? return result
}
執(zhí)行結(jié)果如圖所示:
結(jié)果只花費(fèi)了43ms左右時(shí)間铺根。
方法六:ES6的new Set()
function distinct(a, b) {
? ? returnArray.from(new Set([...a, ...b]))
}
Set數(shù)據(jù)結(jié)構(gòu)宪躯,它類似于數(shù)組,其成員的值都是唯一的位迂。
利用Array.from將Set結(jié)構(gòu)轉(zhuǎn)換成數(shù)組访雪。
結(jié)果如圖所示:
從結(jié)果看出來該方法執(zhí)行只花了151ms左右的時(shí)間。
總結(jié)
從上面幾種方法得出如下結(jié)論:
數(shù)組去重要么使用for...of + Object方式掂林,要么使用ES6的 new Set()方式臣缀。
從執(zhí)行結(jié)果看for...of + Object的效率應(yīng)該是最高的(只在當(dāng)前量級(jí)的計(jì)算結(jié)果來看)。
附錄
附錄中添加瀏覽器Array對(duì)象支持indexOf和forEach的polyfill:
Array.prototype.indexOf = Array.prototype.indexOf ||function(item) {
? ? for(vari = 0, j =this.length; i < j; i++) {
? ? ? ? if(this[i] === item) {
? ? ? ? ? ? return i;
? ? ? ? }
? ? }
? ? return-1;
}
Array.prototype.forEach = Array.prototype.forEach ||function(callback, thisArg) {
? ? if(!callback ||typeofcallback !== 'function')return;
? ? for(vari = 0, j =this.length; i < j; i++) {
? ? ? ? callback.call(thisArg, this[i], i,this);
? ? }
}