前端面試:你以為這只是一個(gè)簡(jiǎn)單的數(shù)組去重嗎?
前言
之前面試過(guò)程被問(wèn)到數(shù)組去重有哪些方式?心想這個(gè)問(wèn)題我會(huì),隨便也能說(shuō)出好幾種,也不帶多思考的。巴拉巴拉巴拉巴拉。說(shuō)完,面試官好像不太滿意的樣子,還問(wèn)了句,沒(méi)了嗎。我想,咋滴,就這些還不不夠用嗎。然后就下一題了。
后來(lái)我看到這樣的數(shù)據(jù),忽然意識(shí)到,之前的面試怕不是草率了,完全沒(méi)考慮復(fù)雜數(shù)據(jù)結(jié)構(gòu),難怪他聽(tīng)完不太滿意的樣子。
let arr = [1, 1, '2', 3, 1, 2,
{ name: '張三', id: { n: 1 }},
{ name: '張三', id: { n: 1 }},
{ name: '張三', id: { n: 2 }}
]
在平時(shí)的開(kāi)發(fā)過(guò)程中這樣的數(shù)據(jù)應(yīng)該少見(jiàn),絕大部分的數(shù)組中數(shù)據(jù)格式應(yīng)該保持一致,特殊情況就不管他了。
今天再來(lái)出一期數(shù)組去重的方式。
基本數(shù)據(jù)類(lèi)型去重
確實(shí)有多種方式,比如set結(jié)構(gòu)轉(zhuǎn)一下,lodash提供的uniq方法,或者自己利用其他方式實(shí)現(xiàn)也是很多的。今天就介紹幾個(gè)
當(dāng)面試官詢問(wèn)數(shù)組去重時(shí),可以考慮到數(shù)組可能包含基本數(shù)據(jù)類(lèi)型、引用數(shù)據(jù)類(lèi)型,或者混合了兩種類(lèi)型的情況。以下是針對(duì)不同情況的多種思路和實(shí)現(xiàn)方法:
使用 Set 數(shù)據(jù)結(jié)構(gòu)(ES6)
const array = [1, 2, 3, 3, 4, 5, 5];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 4, 5]
使用 Array.prototype.filter()
const array = [1, 2, 3, 3, 4, 5, 5];
const uniqueArray = array.filter((item, index) => array.indexOf(item) === index);
console.log(uniqueArray); // [1, 2, 3, 4, 5]
使用 Array.prototype.reduce()
const array = [1, 2, 3, 3, 4, 5, 5];
const uniqueArray = array.reduce((acc, curr) => {
if (!acc.includes(curr)) {
acc.push(curr);
}
return acc;
}, []);
console.log(uniqueArray); // [1, 2, 3, 4, 5]
使用 for 循環(huán)和 Array.prototype.indexOf()
const array = [1, 2, 3, 3, 4, 5, 5];
const uniqueArray = [];
for (let i = 0; i < array.length; i++) {
if (uniqueArray.indexOf(array[i]) === -1) {
uniqueArray.push(array[i]);
}
}
console.log(uniqueArray); // [1, 2, 3, 4, 5]
Lodash 是一個(gè)非常流行的 JavaScript 工具庫(kù),提供了許多實(shí)用的函數(shù)來(lái)簡(jiǎn)化開(kāi)發(fā)過(guò)程。它也包含了一些用于數(shù)組去重的方法。以下是幾種利用 Lodash 實(shí)現(xiàn)數(shù)組去重的方法:
使用 _.uniq() 方法
const _ = require('lodash');
const array = [1, 2, 3, 3, 4, 5, 5];
const uniqueArray = _.uniq(array);
console.log(uniqueArray); // [1, 2, 3, 4, 5]
使用 _.uniqWith() 方法(使用自定義比較函數(shù))
const _ = require('lodash');
const array = [{id: 1}, {id: 2}, {id: 1}];
const uniqueArray = _.uniqWith(array, (a, b) => _.isEqual(a, b));
console.log(uniqueArray); // [{id: 1}, {id: 2}]
2. 數(shù)組包含引用數(shù)據(jù)類(lèi)型:
使用 Set 數(shù)據(jù)結(jié)構(gòu),利用自定義比較函數(shù)(如果需要去重對(duì)象數(shù)組)
const array = [{id: 1}, {id: 2}, {id: 1}];
const uniqueArray = Array.from(new Set(array.map(JSON.stringify)), JSON.parse);
console.log(uniqueArray); // [{id: 1}, {id: 2}]
使用 Array.prototype.reduce()
const array = [{id: 1}, {id: 2}, {id: 1}];
const uniqueArray = array.reduce((acc, current) => {
const isExist = acc.some(item => JSON.stringify(item) === JSON.stringify(current));
if (!isExist) acc.push(current);
return acc;
}, []);
console.log(uniqueArray); // [{id: 1}, {id: 2}]
上面都是使用了JSON.stringfy。但有個(gè)問(wèn)題,如果引用數(shù)據(jù)的順序不一樣,轉(zhuǎn)的string就不等了。所以還是自己實(shí)現(xiàn)一個(gè)方法好一些。
使用自定義比較函數(shù)
這種方法適用于任何類(lèi)型的數(shù)組,包括混合了基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型的數(shù)組。
function uniqueArray(array) {
const seen = new Map();
return array.filter(item => {
if (typeof item === 'object' && item !== null) {
const key = Object.keys(item).sort().map(k => `${k}:${item[k]}`).join('|');
if (!seen.has(key)) {
seen.set(key, true);
return true;
}
} else {
if (!seen.has(item)) {
seen.set(item, true);
return true;
}
}
return false;
});
}
const array = [1, 2, {id: 1}, {id: 2}, 1, {id: 1}];
const uniqueArr = uniqueArray(array);
console.log(uniqueArr); // [1, 2, {id: 1}, {id: 2}]
這個(gè)方法利用了 Map 數(shù)據(jù)結(jié)構(gòu)的特性,用鍵來(lái)存儲(chǔ)數(shù)組中的元素,保證了元素的唯一性。對(duì)于對(duì)象,通過(guò)將屬性名排序并拼接成字符串來(lái)判斷是否重復(fù)。
如果都是引用結(jié)構(gòu),我們平時(shí)也可以使用 _.uniqBy() 方法去重,比如根據(jù)id什么的
const _ = require('lodash');
const array = [{id: 1}, {id: 2}, {id: 1}];
const uniqueArray = _.uniqBy(array, JSON.stringify);
console.log(uniqueArray); // [{id: 1}, {id: 2}]