目錄
無論是在實際開發還是工作面試,數組去重都是一個很常見的問題,今天就來總結一下,以備不時之需。首先聲明這篇博客中出現的所有數組去重方法並不是我自己一個人想到的,在撰寫這篇博客的期間,參考了網上很多資料,希望能幫到大家。
用戶需求:假設有數組 array = [1,5,2,3,4,2,3,1,3,4];要求你要寫一個函數 unique(),使得 array.unique() 或 unique(array) 的值爲 [1,5,2,3,4],也就是把重複的值都去掉,只保留不重複的值。
1、雙重循環方法
雙重循環方法是比較簡單,而且很容易被想到的方法,具體去重步驟如下:
Array.prototype.unique = function () { //數組原型上添加 unique 方法
const newArray = [];
let isRepeat; //編輯元素是否重複
for (let i = 0; i < this.length; i++) { //外層循環遍歷數組
isRepeat = false;
for (let j = i + 1; j < this.length; j++) { //內層循環遍歷查找重複元素
if (this[i] === this[j]) { //如果有相同元素,就跳出當前循環,看下一個元素是否重複
isRepeat = true;
break;
}
}
if (!isRepeat) { //如果不是重複元素,就push到新數組中
newArray.push(this[i]);
}
}
return newArray;
}
console.log([1,1,2,1,3,4,5,4].unique()); //[2,1,3,5,4] 不穩定
缺點:採用上述這種方式返回的新數組,能做到數組去重的效果,但是並不能反映原數組中元素的出現順序,不穩定。
2、indexOf() 檢測元素方法
2.1、方法一:如果索引不是第一個索引,說明是重複值
利用 filter() 方法的過濾功能,indexOf() 方法返回第一個索引值。只將數組中元素第一次出現的返回,之後出現的將被過濾掉
//方法一:
Array.prototype.unique = function () {
return this.filter((item, index) => {
return this.indexOf(item) === index; //數組中每個元素的位置和其第一次出現的下標對應
})
}
console.log([1,1,2,1,3,4,5,4].unique()); //[1,2,3,4] 穩定
2.2、方法二:如果新數組中含有改元素,說明是重複值
Array.prototype.unique = function () {
const newArray = [];
this.forEach(item => {
if (newArray.indexOf(item) === -1) { //如果新數組中不含有當前元素,就push
newArray.push(item);
}
});
return newArray;
}
console.log([1,1,2,1,3,4,5,4].unique()); //[1,2,3,4] 穩定
上述的這兩種方法輸出的數組副本,不會將原數組中的元素出現位置打亂,業界俗稱:穩定。
3、將數組元素先 Sort() 排序
3.1、方法一:先對原數組進行排序,然後再進行元素比較
Array.prototype.unique = function () {
const newArray = [];
this.sort(); //排序
for (let i = 0; i < this.length; i++) { //對排序數組進行遍歷
if (this[i] !== this[i + 1]) { //當前元素和後面的元素不相同就push
newArray.push(this[i]);
}
}
return newArray;
}
3.2、和新數組的最後一個元素進行比較,如果相同就說明是重複元素。
Array.prototype.unique = function () {
const newArray = [];
this.sort(); //排序
for (let i = 0; i < this.length; i++) {
if (this[i] !== newArray[newArray.length - 1]) { //和新數組的最後一個元素比較
newArray.push(this[i]);
}
}
return newArray;
}
上述的這兩種方法由於都用到 sort() 方法, 會打亂原有數組的順序,所以:不穩定
4、檢測新數組中元素 includes()
Array.prototype.unique = function () {
const newArray = [];
this.forEach(item => {
if (!newArray.includes(item)) { //如果新數組中含有當前元素,就說明是重複值
newArray.push(item);
}
});
return newArray;
}
console.log([1,1,2,1,3,4,5,4].unique()); //[1,2,3,4] 穩定
5、配合 sort() 使用 reduce() 去重
Array.prototype.unique = function () {
return this.sort().reduce((init, current) => {
if(init.length === 0 || init[init.length - 1] !== current){ //類似於3.2方法
init.push(current);
}
return init;
}, []);
} //只要涉及到 sort 方法,就是不穩定的
6、Map
JavaScript 的對象(Object),本質上是鍵值對的集合(Hash 結構),但是傳統上只能用字符串當作鍵。這給它的使用帶來了很大的限制。爲了解決這個問題,ES6 提供了 Map 數據結構。它類似於對象,也是鍵值對的集合,但是 “鍵” 的範圍不限於字符串,各種類型的值(包括對象)都可以當作鍵。也就是說,Object 結構提供了 “字符串—值” 的對應,Map 結構提供了 “值—值” 的對應,是一種更完善的 Hash 結構實現。
Map 對象保存鍵值對,並且能夠記住鍵的原始插入順序。和 Object 類型不同(key 只能是字符串),在 Map 對象中任何值(對象或者原始值) 都可以作爲一個鍵或一個值。詳請移步:mdn 文檔
使用 Map 對象實現數組去重的主要思路:創建一個空 Map,遍歷原始數組,把數組的每一個元素作爲 key 存到 Map 對象中,因爲 Map 中不會出現相同的 key 值,所以最終得到的 Map 中的所有 key 值就是去重後的結果。
function unique(arr) {
let hashMap = new Map(); //創建一個新的Map對象
let result = new Array(); // 數組用於返回結果
for (let i = 0; i < arr.length; i++) {
if(hashMap.has(arr[i])) { // 判斷 hashMap 中是否已有該 key 值
hashMap.set(arr[i], true); // 後面的true 代表該 key 值在原始數組中重複了,false反之
} else { // 如果 hashMap 中沒有該 key 值,添加
hashMap.set(arr[i], false);
result.push(arr[i]);
}
}
return result;
}
console.log(unique([1, 1, 1, 2, 3, 3, 4, 5, 5, "a", "b", "a"])); // [ 1, 2, 3, 4, 5, 'a', 'b' ]
7、Set
Set 對象允許你存儲任何類型的 唯一值,無論是原始值或者是對象引用,該方法天生就適合數組去重。mdn 文檔
不成,NaN 和 undefined 都可以被存儲在 Set 中, NaN 之間被視爲相同的值(NaN被認爲是相同的,儘管 NaN !== NaN)
Array.prototype.unique = function () {
const set = new Set(this); //new 一個 Set 對象,將數組作爲參數傳遞進去
return Array.from(set); //自動實現去重,Array.from() 將其轉化爲數組
} //可以通過 set.add(args) 添加元素
console.log([1,1,2,1,3,4,5,4].unique()); //[1,2,3,4] 穩定
Array.prototype.unique = function () {
return [...new Set(this)]; //簡化上述步驟
}
console.log([1,1,2,1,3,4,5,4].unique()); //[1,2,3,4] 穩定