JS數(shù)據(jù)去重的7種實用方法總結(jié)(附完整代碼與原理)
前言
在 JavaScript 開發(fā)中,“數(shù)據(jù)去重” 是高頻需求 —— 比如處理接口返回的重復(fù)列表、用戶輸入的重復(fù)值等。不同場景下,適合的去重方法不同,有的追求簡潔,有的追求性能,有的需要兼容復(fù)雜數(shù)據(jù)類型。今天就整理 7 種常用的去重方法,每種都附帶代碼和核心原理,幫你輕松應(yīng)對各種去重場景。
一、基礎(chǔ)方法:利用 Set 去重(最簡潔)
核心原理:ES6 新增的 Set 集合本身具有 “值唯一” 的特性,能自動忽略重復(fù)值,再通過擴展運算符(...)或 Array.from() 轉(zhuǎn)回數(shù)組即可。
代碼實現(xiàn)
// 待去重數(shù)組(包含數(shù)字、字符串、重復(fù)值) const arr = [1, 2, 2, "3", "3", 4, 4, 5]; // 方法 1:擴展運算符轉(zhuǎn)數(shù)組 const uniqueArr1 = [...new Set(arr)]; // 方法 2:Array.from() 轉(zhuǎn)數(shù)組(兼容不支持擴展運算符的環(huán)境) const uniqueArr2 = Array.from(new Set(arr)); console.log(uniqueArr1); // 輸出:[1, 2, "3", 4, 5] console.log(uniqueArr2); // 輸出:[1, 2, "3", 4, 5]
優(yōu)缺點
優(yōu)點:代碼最簡潔,一行搞定;性能優(yōu)秀(底層是哈希表實現(xiàn),時間復(fù)雜度 O (n));
缺點:無法區(qū)分 “值相同但類型不同” 的數(shù)據(jù)(比如
1和"1"會被視為不同值,這其實是合理的,若需強制相同需額外處理);不支持對象 / 數(shù)組等引用類型的去重(引用不同會被視為不同值)。
適用場景
處理簡單數(shù)據(jù)類型(數(shù)字、字符串、布爾值)的數(shù)組,追求代碼簡潔和性能。
二、傳統(tǒng)方法:利用 indexOf/includes 去重
核心原理:創(chuàng)建一個新數(shù)組,遍歷原數(shù)組,判斷當前元素是否在新數(shù)組中(用 indexOf 或 includes),不在則加入新數(shù)組,最終得到去重后的數(shù)組。
代碼實現(xiàn)(indexOf 版)
const arr = [1, 2, 2, 3, 3, 3];
const uniqueArr = [];
for (let i = 0; i < arr.length; i++) {
// indexOf 返回 -1 表示元素不在新數(shù)組中
if (uniqueArr.indexOf(arr[i]) === -1) {
uniqueArr.push(arr[i]);
}
}
console.log(uniqueArr); // 輸出:[1, 2, 3]代碼實現(xiàn)(includes 版,更直觀)
const arr = [1, 2, 2, 3, 3, 3];
const uniqueArr = [];
for (let item of arr) {
// includes 直接返回布爾值,判斷元素是否存在
if (!uniqueArr.includes(item)) {
uniqueArr.push(item);
}
}
console.log(uniqueArr); // 輸出:[1, 2, 3]優(yōu)缺點
優(yōu)點:兼容性好(支持 ES5 及以上,無需擔心環(huán)境問題);邏輯直觀,容易理解;
缺點:性能較差(
indexOf/includes每次都會遍歷新數(shù)組,時間復(fù)雜度 O (n²),數(shù)據(jù)量大時會卡頓);同樣不支持引用類型去重。
適用場景
處理小規(guī)模的簡單類型數(shù)組,或需要兼容低版本瀏覽器的場景。
三、優(yōu)化性能:利用對象鍵名唯一性去重
核心原理:對象的鍵名(key)具有唯一性(不能重復(fù)),遍歷原數(shù)組時,把元素作為對象的鍵名存儲,同時判斷鍵名是否已存在,不存在則加入新數(shù)組。
代碼實現(xiàn)
const arr = [1, 2, 2, "2", 3, 3];
const uniqueArr = [];
const tempObj = {}; // 臨時對象,用于存儲已存在的元素
for (let item of arr) {
// 把元素轉(zhuǎn)為字符串作為鍵名(避免 1 和 "1" 被視為不同鍵)
const key = typeof item + item;
if (!tempObj[key]) {
tempObj[key] = true; // 標記為已存在
uniqueArr.push(item);
}
}
console.log(uniqueArr); // 輸出:[1, 2, "2", 3]關(guān)鍵細節(jié)
為什么要加
typeof item?比如1(數(shù)字)和"1"(字符串),直接用item當鍵名會都是"1",導(dǎo)致誤判;加typeof后,鍵名會變成"number1"和"string1",能正確區(qū)分。若想強制把
1和"1"視為相同值,可去掉typeof,直接用const key = item。
優(yōu)缺點
優(yōu)點:性能優(yōu)秀(對象的鍵名查詢是哈希表操作,時間復(fù)雜度 O (n));支持區(qū)分 “值相同類型不同” 的場景;
缺點:需額外處理鍵名類型(避免誤判);不支持引用類型去重(對象作為鍵名會被轉(zhuǎn)為
[object Object],導(dǎo)致所有對象都被視為相同)。
適用場景
處理大規(guī)模的簡單類型數(shù)組,追求高性能,且需要區(qū)分值類型。
四、進階方法:利用 filter + indexOf 去重
核心原理:filter 方法會篩選出滿足條件的元素,結(jié)合 indexOf 判斷 “當前元素在原數(shù)組中的第一次出現(xiàn)位置是否等于當前索引”—— 如果等于,說明是第一次出現(xiàn),保留;否則是重復(fù)值,過濾掉。
代碼實現(xiàn)
const arr = [1, 2, 2, 3, 3, 4];
// filter 回調(diào)函數(shù):返回 true 則保留元素
const uniqueArr = arr.filter((item, index) => {
// indexOf 會返回元素在數(shù)組中第一次出現(xiàn)的索引
return arr.indexOf(item) === index;
});
console.log(uniqueArr); // 輸出:[1, 2, 3, 4]優(yōu)缺點
優(yōu)點:代碼簡潔(一行搞定);邏輯優(yōu)雅,適合函數(shù)式編程風格;
缺點:性能差(
filter遍歷一次,indexOf每次又遍歷一次,時間復(fù)雜度 O (n²));不支持引用類型去重。
適用場景
處理小規(guī)模簡單類型數(shù)組,追求函數(shù)式編程風格,不關(guān)心極致性能。
五、ES6 進階:利用 Map 去重
核心原理:Map 的鍵(key)可以是任意類型,且具有唯一性。遍歷原數(shù)組時,用 Map.has() 判斷元素是否已存在,不存在則用 Map.set() 存儲,并加入新數(shù)組。
代碼實現(xiàn)
const arr = [1, 2, 2, "3", "3", 4];
const uniqueArr = [];
const tempMap = new Map();
for (let item of arr) {
if (!tempMap.has(item)) { // 判斷元素是否已在 Map 中
tempMap.set(item, true); // 存儲元素(值隨便設(shè),只要有標記即可)
uniqueArr.push(item);
}
}
console.log(uniqueArr); // 輸出:[1, 2, "3", 4]優(yōu)缺點
優(yōu)點:性能優(yōu)秀(時間復(fù)雜度 O (n));支持更多鍵類型(比如
NaN,Set也支持,但indexOf不支持NaN的判斷);缺點:代碼比
Set稍繁瑣;不支持引用類型去重(引用不同會被視為不同鍵)。
適用場景
處理簡單類型數(shù)組,或需要存儲 NaN 等特殊值的場景(indexOf 無法判斷 NaN,但 Map.has(NaN) 可以)。
六、復(fù)雜場景:引用類型(對象 / 數(shù)組)去重
前面的方法都無法處理對象 / 數(shù)組等引用類型(比如 { id: 1 } 和 { id: 1 } 會被視為不同值,因為引用不同)。此時需要通過 “比較內(nèi)容” 來實現(xiàn)去重,常用方法是 JSON.stringify 轉(zhuǎn)字符串 或 手動比較屬性。
方法 1:JSON.stringify 轉(zhuǎn)字符串(簡單對象適用)
核心原理:把對象轉(zhuǎn)為 JSON 字符串,再用 Set 或?qū)ο箧I名去重(字符串可以被唯一識別)。
// 待去重的對象數(shù)組(id 相同視為重復(fù))
const arr = [
{ id: 1, name: "小明" },
{ id: 1, name: "小明" }, // 重復(fù)
{ id: 2, name: "小紅" }
];
const uniqueArr = [...new Set(arr.map(item => JSON.stringify(item)))]
.map(str => JSON.parse(str)); // 轉(zhuǎn)回對象
console.log(uniqueArr);
// 輸出:[{ id: 1, name: "小明" }, { id: 2, name: "小紅" }]方法 2:手動比較屬性(復(fù)雜對象適用)
核心原理:指定一個唯一標識(比如 id),遍歷數(shù)組時,判斷新數(shù)組中是否已有該標識的對象,沒有則加入。
const arr = [
{ id: 1, name: "小明" },
{ id: 1, name: "小明" },
{ id: 2, name: "小紅" }
];
const uniqueArr = [];
for (let item of arr) {
// 用 some 判斷新數(shù)組中是否已有相同 id 的對象
const isRepeat = uniqueArr.some(uniqueItem => uniqueItem.id === item.id);
if (!isRepeat) {
uniqueArr.push(item);
}
}
console.log(uniqueArr);
// 輸出:[{ id: 1, name: "小明" }, { id: 2, name: "小紅" }]優(yōu)缺點
優(yōu)點:能處理引用類型的去重;
缺點:
JSON.stringify有局限性(比如無法處理function、undefined、Symbol等屬性);手動比較屬性需指定唯一標識,靈活性較低。
適用場景
處理對象數(shù)組,且對象結(jié)構(gòu)簡單(無特殊屬性),或有明確唯一標識(如 id)的場景。
七、排序后去重:利用 sort + 相鄰比較
核心原理:先通過 sort() 把數(shù)組排序(相同元素會相鄰),再遍歷排序后的數(shù)組,比較當前元素和前一個元素,不同則加入新數(shù)組。
代碼實現(xiàn)
const arr = [3, 1, 2, 2, 3, 1, 4];
const uniqueArr = [];
// 先排序(注意:sort 默認按字符串排序,數(shù)字需加比較函數(shù))
const sortedArr = arr.sort((a, b) => a - b); // 排序后:[1, 1, 2, 2, 3, 3, 4]
for (let i = 0; i < sortedArr.length; i++) {
// 比較當前元素和前一個元素,不同則保留
if (sortedArr[i] !== sortedArr[i - 1]) {
uniqueArr.push(sortedArr[i]);
}
}
console.log(uniqueArr); // 輸出:[1, 2, 3, 4]優(yōu)缺點
優(yōu)點:邏輯簡單,適合已排序或可排序的數(shù)組;
缺點:會改變原數(shù)組的順序(若需保留原順序則不適用);性能受排序影響(
sort時間復(fù)雜度約 O (n log n));不支持引用類型去重。
適用場景
處理數(shù)字數(shù)組,且不關(guān)心原數(shù)組順序的場景。
總結(jié):不同場景如何選?
| 場景需求 | 推薦方法 | 時間復(fù)雜度 |
|---|---|---|
| 簡單類型 + 代碼簡潔 | Set 去重 | O(n) |
| 簡單類型 + 高性能 + 大規(guī)模 | 對象鍵名去重 / Map 去重 | O(n) |
| 低版本瀏覽器兼容 | indexOf/includes 去重 | O(n²) |
| 函數(shù)式編程風格 | filter + indexOf 去重 | O(n²) |
| 對象數(shù)組(有唯一標識) | 手動比較屬性去重 | O(n²) |
| 數(shù)字數(shù)組(不關(guān)心順序) | sort + 相鄰比較去重 | O(n log n) |
記住:沒有 “最好” 的去重方法,只有 “最適合” 的 —— 根據(jù)數(shù)據(jù)類型、數(shù)據(jù)規(guī)模和環(huán)境需求選擇即可!
到此這篇關(guān)于JS數(shù)據(jù)去重的7種實用方法總結(jié)的文章就介紹到這了,更多相關(guān)JS數(shù)據(jù)去重方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript showModalDialog,open取得父窗口的方法
showModalDialog,open取得父窗口的代碼,需要的朋友可以參考下。2010-03-03
通過Javascript讀取本地Excel文件內(nèi)容的代碼示例
這篇文章主要介紹了通過Javascript讀取本地Excel文件內(nèi)容的代碼示例,但需要一定的條件才可以使用js操作本地文件,需要的朋友參考下吧2014-04-04
JavaScript數(shù)組去重由慢到快由繁到簡(優(yōu)化篇)
本文給大家介紹通過indexof去重,hash去重,排序后去重及set去重由慢到快有繁到簡的方法給大家介紹了js數(shù)組去重的方法,非常不錯,具有參考借鑒價值,感興趣的朋友一起看看吧2016-08-08

