如何用JavaScript進行數組去重

今天的文章和你們談一談如何用JavaScript進行數組去重,這是一道常見的面試(筆試)題,能夠很好地考察出一我的的邏輯思惟及邊界考慮狀況,但願此文可以幫助你們在解決相似問題時拓寬思路。據我到目前爲止面試的狀況,不多有人能在現場考慮很全,基本上的人都是淺嘗輒止。面試

固然,「使用庫中的一個函數就能去重」並不在本篇文章的討論範圍內,咱們針對的是須要本身寫代碼的場景。考慮到實際狀況,咱們使用ES5(主要就用了indexOf方法,若是是更古老的環境,能夠本身增長這段代碼,或者使用ES5兼容庫es5-sham.js)。算法

咱們先審題:數組,題目中並無說是什麼樣的數組,即數組的組成元素多是字符串、數字、布爾、數組、對象、Null、Undefined。數組

在開始以前咱們先看看這些類型以及他們的值比較關係:函數

接着咱們來看看數組中的indexOf方法:es5

var gtArray = [66],
gtObject = {
id: 1
},
gtTestArr = ["1", 1, true, [66],
gtArray, { id: 1 }, gtObject, null, undefined];

gtTestArr.indexOf("1");
// 0
gtTestArr.indexOf(1); // 1
gtTestArr.indexOf(true); // 2
gtTestArr.indexOf([66]); // -1
gtTestArr.indexOf(gtArray); // 4
gtTestArr.indexOf({ id: 1 }); //
-1
gtTestArr.indexOf(gtObject); // 6
gtTestArr.indexOf(null); // 7
gtTestArr.indexOf(undefined); //
8

 

從上述效果中看咱們能夠得出結論:indexOf 能夠幫咱們找到一個數組中某個元素(若該元素爲數組或者對象,則爲該引用的地址值)對應的索引值,在人腦「看」來相同的[66] 和 gtArray,實際上除了都用gtArray表示的部分是同樣的,其餘的 [66]之間以及gtArray都是不一樣的引用地址,天然也就找不到索引值啦 。code

 

好了,迴歸正題,咱們要進行數組去重,那麼先想個大體的思路,好比:對象

1)新建一個空數組,老數組從第一個開始,看看新數組中有沒有,若是沒有就push進入新數組,若是存在就下一個。索引

2)在一個數組裏面從第一個開始,將它後面的元素依次與當前這個比較,若是相等,就把後面的那個元素刪掉,依次往復操做,直到最後一個。接着比較對象變成第二個,重複上述步驟,直到比較對象是最後一個。ip

3)and so on字符串

 

固然每一個思路有不一樣的算法,對於一種判斷描述也能夠有不一樣的實現方式(以下面的相等),好比用 map,用下標等。不一樣方式可能也會有不一樣的侷限性或者前置條件。

 

好,如今咱們界定一下什麼是相等,簡單的 1 與 1 確定是相等的,而1 與 「1」是不等的,對於引用類型咱們能夠分爲幾種模式(級別):

1)僅引用地址同樣纔算相等。

2)引用地址能夠不同,但對應的數組(對象)所擁有的元素(鍵值對)如出一轍就算相等。 即在咱們看來,這兩個數據寫出來,看上去就是同樣的。

3)對因而非數組的對象,針對幾個key的值是同樣的狀況,咱們將其認定是同樣的。好比 { id: 1, name: 」張三」 } 和 { id: 1, name: 」李四」 } 在只考慮 id 字段時他們就是同樣的。固然這種類型是咱們人爲賦予的模式。

 

好了,準備工做已作好,咱們開始碼代碼吧。

按照思路1,相等的模型取第二種,直接上代碼以下:

function gtUniqueArr(arr) {
var i,
newArr = [];

function isExist(_item, _arr) {
var k,
find = false;
if (typeof _item !== "object"
|| _item === null) {
return _arr.indexOf(_item) > -1;
}
for (k in _arr) {
if (_arr.hasOwnProperty(k)) {
find = isEqual(_item, _arr[k]);
if (find) {
break;
}
}
}
return find;
};
function isEqual(_a, _b) {
var k,
keysA,
keysB,
equal = true;
if (typeof _a !== "object" ||
_a === null ||
typeof _b !== "object" || _b === null) { // 有非引用類型(數組與對象)或者有NULL類型時直接判斷
return _a === _b;
}
// _a _b 不一樣爲數組或者對象時 直接認爲不一樣,不然長得像數組的對象也會互判相等
if (_a instanceof Array !== _b
instanceof Array) {
return false;
}
// 同爲對象或者數組
keysA = Object.keys(_a);
keysB = Object.keys(_b);
if (keysA.length !== keysB.length) { //
元素量不一樣確定就不是同樣了啊
return false;
}
// 其實也能夠先判斷下引用地址是否同樣,同樣確定就相等啦
for (k in _a) {
if (_a.hasOwnProperty(k)) {
equal = isEqual(_a[k], _b[k]);
if (!equal) {
break;
}
}
}
return equal;
};

if (arr && arr.length) {
for (i = 0; i < arr.length; i++) {
if (!isExist(arr[i], newArr)) {
newArr.push(arr[i]);
}
}
}
return newArr;
}

咱們能夠把isExist,isEqual提取成公共函數,按照思路2,相等類型依然爲第二種,上代碼:

function gtUniqueArr(arr) {
var i, j;

if (arr && arr.length) {
for (i = 0; i < arr.length; i++) {
for (j = i + 1; j < arr.length;
j++) {
if (isEqual(arr[i], arr[j])) {
arr.splice(j, 1);
j--; // 復緣由數組刪除致使的遺漏了的元素指向
}
}
}
}
return arr;
}

固然,要採起不一樣的相等模式,只要改變 isEqual 函數便可,此處其餘兩種相等模式(或者你還有其餘假設的相等模式)訴求相對較少,此處便再也不展開敘述了(模式1,直接用===比較二者便可;模式3,用===檢測要求的字段的值是否同樣)。

當咱們的環境是ES6時,通常的去重標準可使用 set 來作:

var rs = new Set(arr);

可是當數組元素爲引用類型時,引用地址不同但在咱們看來是徹底同樣的兩個元素,這個方法是去不掉的。

相關文章
相關標籤/搜索