最近公司遇到的一個需要用到物件陣列去重的需求,這還不簡單?經過長達十數分鐘的掙扎,emm...,還是去網上粘一個吧...
下了班,越想越氣,我已經菜到這種程度了? 痛定思痛,最終在週末花了一下午的時間整理了下物件陣列去重相關的方法
在公司的時候第一個想到的就是雙重for迴圈了,實現下。
let arr = [
{id: 1, name: 'lsm'},
{id: 2, name: 'mjl'},
{id: 1, name: 'lsm'}
]
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i].id === arr[j].id) {
arr.splice(j, 1)
j--
}
}
}
console.log('arr--', arr)
實現倒是實現了,但是看著這麼一大坨......,不行啊,咱寫程式碼要優雅。換方法
想了半天,終於想出來半個,沒錯外層的forEach就是我想出來的.....
都是淚啊,不說了,上程式碼!
let arr1 = [
{id: 1, name: 'lsm'},
{id: 2, name: 'mjl'},
{id: 1, name: 'lsm'}
]
let newArr1 = []
arr1.forEach((item, index) => {
arr1.findIndex(el => el.id == item.id) == index && newArr1.push(item)
})
console.log('newArr1', newArr1)
emm,真不錯,優雅
我來解釋下這段程式碼:首先我們先定義一個空陣列newArr1,然後通過forEach遍歷arr1,。當拿到陣列中的每個物件之後,開始進行去重。這裡使用findIndex函數根據物件中的id去重,當內層迴圈(findIndex)中物件的id等於外層迴圈中物件的id的時候,返回內層迴圈該符合條件物件的索引,並且拿該索引與外層迴圈當前索引進行比較。如果相等就把物件push到newArr1中,當遍歷到第三個物件的時候,外層index = 2; 但是內層迴圈中符合條件的索引為0,因此不進行push操作,達到去重效果。
總結來說 :進行雙重回圈,內層迴圈根據findInde函數的特性,找到第一個符合條件的值的索引並與外層索引比較。當有重複的物件時,內層迴圈的索引與外層迴圈的索引並不一致,實現去重。
findIndex作用:找到遍歷的陣列中的第一個符合判斷條件的值,並返回該值對應的索引,停止遍歷
作為一名優秀的程式設計師,只會兩種去重方式怎麼行,繼續去粘,,,繼續總結...
let arr2 = [
{id: 1, name: 'lsm'},
{id: 2, name: 'mjl'},
{id: 1, name: 'lsm'}
]
arr2 = arr2.filter((item, index) => {
return arr2.findIndex(el => el.id == item.id) == index
})
console.log('arr3--', arr2)
說明下:這種方式,和上述的那種方式是很類似,內層都是用到findIndex的特性,找到內外層迴圈一致的索引。但是外層用的是filter函數,用該函數的好處,我們不需要單獨再去重新定義一個新的陣列。根據該函數的特性,返回一個包含符合條件物件的陣列
filter作用:實現陣列過濾。判斷filter回撥函數中的條件是否為true,如果為true,返回該遍歷項,最終包裝到一個陣列中統一返回
在總結完上述三個方法之後,我就在想了:陣列去重,如果這個陣列是個基本資料型別的陣列,我們只要遍歷一層,迴圈體裡面只要配合indexOf、includes等方法,就可以找出符合條件的值了,程式碼如下:
let arr = [ 1, 1, 1, "1", "lsm", "52", 2, 81, 2, 81]
let newArr = []
arr.map((item, index) => {
//!newArr.includes(item) && newArr.push(item)
newArr.indexOf(arr1[i]) === -1 && newArr.push(arr1[i])
})
console.log('newArr--', newArr)
但是,如果是參照型別的陣列,我們沒法通過indexOf、includes直接找到符合條件的值。只能通過雙重回圈的方式,通過內層的迴圈找出符合條件的值。而且根據上述案列,我們可以總結出,內層的迴圈必須要返回一個具體的值用於外層的判斷。那some可不可以呢,some也是返回一個具體的boolean,上程式碼
let arr3 = [
{id: 1, name: 'lsm'},
{id: 2, name: 'mjl'},
{id: 1, name: 'lsm'}
]
let newArr3 = []
arr3.forEach((item, index) => {
!newArr3.some(el => el.id == item.id) && newArr3push(item)
})
console.log('newArr2', newArr2)
經過測試,去重成功,我真是人才呀...
說明一下:思路還是雙重回圈,內層迴圈使用some,根據some特性,判斷newArr3中有沒有物件的id等於當前外層物件中的id,沒有的時候返回false,取反,左側返回true,執行右側的push,當遍歷到第三個物件的時候,內層some判斷為true(newArr3中有了id判斷相等的物件),不執行push操作,達到去重效果
some作用:判斷一個陣列中有沒有符合條件的值,只要陣列中有一項符合條件返回true,但是不會終止迴圈,可以使用return終止迴圈(該例中並沒用到該特性)
其實在寫這段程式碼的時候,寫著寫著就感覺不太對勁了。從程式碼中不難看出,內層的some的第一次迴圈newArr3是一個空陣列,遍歷的每一項el是沒有值的,是一個undefined,然後el.id 相當於 undefined.id ??? 系統不會報錯的嗎?
其實事實是這樣的:當我們使用陣列方法的時候,會先去執行該方法,然後會判斷有沒有遍歷項,當沒有遍歷項的時候(陣列為空)壓根就不會進行迴圈,但是由於方法是執行了的,會有一個返回值,該返回值具體是多少,根據使用的陣列方法而定。 比如:
按照上面的總結,想進行參照型別陣列去重,得進行雙重for迴圈,內層迴圈要有一個具體的返回值。外層迴圈用來提供去重的物件,可以使用forEach,map等單純提供遍歷的方法,但是需要重新定義一個新的陣列接收符合條件的物件。也可以使用filter,根據條件,直接返回一個新的陣列,不需要重新定義一個新的陣列,也不用進行push操作。和findIndex方法類似的還有find方法,程式碼如下
let arr4 = [
{id: 1, name: 'lsm'},
{id: 2, name: 'mjl'},
{id: 1, name: 'lsm'}
]
arr4 = arr4.filter(item => {
return arr4.find(el => el.id == item.id) === item
})
console.log('arr4', arr4)
和findIndex方法類似,只不過find返回的是符合條件的遍歷項。然後拿該返回項與當前外層的物件比較,物件比較的是兩者的參照地址,當遍歷到第三層物件的時候,內層遍歷出來的實際上是第一個物件,二者的參照地址是不相同的,從而達到去重的效果
這裡不推薦使用find,因為find方法去重,是在陣列中的物件地址都不相同的前提下,如果程式碼中有如下操作則達不到去重效果
let obj = {id: 10, name: 'lsm'}
let arr = []
arr.push(obj)
arr.push(obj)
//arr中的兩個物件參照地址實際上是一樣
//Set不能對物件去重,但是這種情況可以
find作用:找出符合判斷條件的第一項,並進行返回
可能大家看到這裡就在吐槽了,上面總結的時候為什麼,forEach和Filter要混著舉例。但是其實寫這篇文章,最終目的並不是給大家例舉出各種情況,而是想讓大家瞭解除參照資料型別陣列的去重思路。陣列的遍歷方法有很多,可以有很多中搭配方式實現去重,只是單純靠背的話,總有一天會忘,就像我這隻菜菜,只會個for迴圈......
最終總結:
1. 實現參照型別陣列去重,主要靠雙重回圈。
2. 外層的迴圈可以是forEach、map這種方法,單純的給內層迴圈提供去重物件。這要我們在最外面定義一個新的陣列,用來存放符合條件的陣列。也可以用filter方法,根據filter的特性返回符合條件的陣列,不用自定義新陣列。
3. 內層的函數實現去重,並且內層的函數要有一個有具體含義的返回值,用於外層函數的判斷。可以是some,findIndex,find等。
具體情況是不是與上述總結一直呢。我們最後使用map和some、find、findexIndex再來證明一遍
let arr5 = [
{id: 1, name: 'lsm'},
{id: 2, name: 'mjl'},
{id: 1, name: 'lsm'}
]
let newArr5 = []
arr5.map((item, index) => {
// !newArr5.some(el => el.id == item.id) && newArr5.push(item)
// arr5.findIndex(el => el.id === item.id) === index && newArr5.push(item)
arr5.find(el => el.id === item.id) === item && newArr5.push(item)
})
console.log('arr5', newArr5)
有問題嗎?沒有問題。no problem。我真是個人才。。。
感謝大家能一直看到這個地方,因為是第一次寫文章,寫的不好的地方,還請包涵;寫的不對的地方,還請指正。再次感謝!