JavaScript常用數組去重的方法及對比詳解
前言
數組去重在面試和工作中都是比較容易見到的問題。
這篇文章主要是來測試多個方法,對下面這個數組的去重結果進行分析討論。如果有不對的地方,還請大傢指出。
const arr = [ 1, 1, "1", "1", 0, 0, "0", "0", true, false, "true", "false", "a", "A", undefined, undefined, "undefined", null, null, 'null', NaN, NaN, +0, -0, new String("1"), new String("1"), Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], [] ];
特殊類型
console.log(1 == "1"); // true console.log(1 === "1"); // false console.log(0 == "0"); // true console.log(0 === "0"); // false console.log(0 == +0); // true console.log(0 === +0); // true console.log(0 == -0); // true console.log(0 === -0); // true console.log(+0 == -0); // true console.log(+0 === -0); // true console.log(0 == false); // true console.log(0 === false); // false console.log(0 == undefined); // false console.log(0 === undefined); // false console.log(0 == null); // false console.log(0 === null); // false console.log(1 == true); // true console.log(1 === true); // false console.log(undefined == null); // true console.log(undefined === null); // false console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(new String("1") == new String("1")); // false console.log(new String("1") === new String("1")); // false Object.prototype.toString.call(new String('1')) // '[object String]' console.log(/a/ == /a/); // false console.log(/a/ === /a/); // false Object.prototype.toString.call(/a/); //'[object RegExp]' console.log(Symbol(1) == Symbol(1)); // false console.log(Symbol(1) === Symbol(1)); // false console.log({} == {}); // false console.log({} === {}); // false console.log([] == []); // false console.log([] === []); // false
接下來,我們看看下面多個去重方法,對以上特殊類型的去重效果。
代碼一(暴力解法)
// 暴力解法一 function unique(array) { if (!Array.isArray(array)) { console.log("type error!"); return; } const res = [array[0]]; let arrLen = array.length; let resLen = res.length; for (let i = 0; i < arrLen; i++) { let flag = true; for (let j = 0; j < resLen; j++) { if (array[i] === res[j]) { flag = false; break; } } if (flag) { res.push(array[i]); resLen = res.length; } } return res; } // [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]
輸出:
[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]
輸出結果說明:
- 去重
+0
、-0
、0
NaN
不去重- 對象
new String("1")
、/a/
、{}
不去重 - 數組
[]
不去重 Symbol(1)
不去重
暴力解法,簡單易理解,兼容性好。去重結果如上所示。
代碼二(ES6)
// ES6 Array.from + Set 方法一 function unique(array) { if (!Array.isArray(array)) { console.log('type error!') return } return Array.from(new Set(array)) } // ES6 點運算 + Set 方法二 function unique1(array) { if (!Array.isArray(array)) { console.log('type error!') return } return [...new Set(arr)] } // ES6 箭頭函數 + 點運算 + Set 方法三 const unique2 = (array) => { if (!Array.isArray(array)) { console.log('type error!') return } return [...new Set(arr)] } // ES6 Map + ES5 filter 方法四 function unique3(array) { if (!Array.isArray(array)) { console.log('type error!') return } const seen = new Map() return array.filter((a) => !seen.has(a) && seen.set(a, 1)) }
輸出:
[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]
輸出結果說明:
- 去重
+0
、-0
、0
- 去重
NaN
- 對象
new String("1")
、/a/
、{}
不去重 - 數組
[]
不去重 Symbol(1)
不去重
代碼三(indexOf + forEach)
利用indexOf檢測元素在新數組是否存在
// indexOf + forEach 利用indexOf檢測元素在新數組是否存在 function unique(array) { if (!Array.isArray(array)) { console.log('type error!') return } const newArr = []; array.forEach((el) => { if (newArr.indexOf(el) === -1) { newArr.push(el); } }); return newArr; }
輸出:
[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]
輸出結果說明:
- 去重
+0
、-0
、0
NaN
不去重- 對象
new String("1")
、/a/
、{}
不去重 - 數組
[]
不去重 Symbol(1)
不去重
代碼四(indexOf + filter)
利用indexOf檢測元素在數組中第一次出現的位置是否和元素現在的位置相等
// indexOf + forEach 利用indexOf檢測元素在新數組是否存在 function unique(array) { if (!Array.isArray(array)) { console.log('type error!') return } return array.filter((item, index) => { return array.indexOf(item) === index; }); } console.log([NaN].indexOf(NaN)); // -1
輸出:
[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]
輸出結果說明:
- 去重
+0
、-0
、0
- 兩個
NaN
都會被刪除 - 對象
new String("1")
、/a/
、{}
不去重 - 數組
[]
不去重 Symbol(1)
不去重
重點:
console.log([NaN].indexOf(NaN)); // -1
代碼五(sort排序,不支持Symbol)
sort()方法主要是用於對數組進行排序,默認情況下該方法是將數組元素轉換成字符串,然後按照ASC碼進行排序
// sort()方法不支持Symbol,Symbol不支持轉換成字符串 function unique(array) { if (!Array.isArray(array)) { console.log("type error!"); return; } const sortArr = array.sort(); const newArr = []; sortArr.forEach((el, i) => { if (sortArr[i] !== sortArr[i - 1]) { newArr.push(el); } }); return newArr; }
輸出:
[[], [], /a/, /a/, 0, "0", 0, 1, "1", String {'1'}, String {'1'}, "A", NaN, NaN, {}, {}, "a", false, "false", null, "null", true, "true", "undefined", undefined]
輸出結果說明:
+0
、-0
、0
、"0"
位置不同會導致去重不瞭NaN
不去重- 對象
new String("1")
、/a/
、{}
不去重 - 數組
[]
不去重 - sort()方法不支持處理含有
Symbol
的數組
代碼六(includes)
利用includes()方法檢查新數組是否包含原數組的每一項
// 利用includes()方法檢查新數組是否包含原數組的每一項 function unique(array) { if (!Array.isArray(array)) { console.log("type error!"); return; } const newArr = []; array.forEach((el) => { newArr.includes(el) ? newArr : newArr.push(el); }); return newArr; }
輸出:
[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]
輸出結果說明:
- 去重
+0
、-0
、0
- 去重
NaN
- 對象
new String("1")
、/a/
、{}
不去重 - 數組
[]
不去重 Symbol
不去重
代碼七(includes+reduce)
利用includes()方法檢查新數組是否包含原數組的每一項
// 利用includes()方法檢查新數組是否包含原數組的每一項 function unique(array) { if (!Array.isArray(array)) { console.log("type error!"); return; } return array.reduce((pre, cur) => { !pre.includes(cur) && pre.push(cur); return pre; }, []); }
輸出:
[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []]
輸出結果說明:
- 去重
+0
、-0
、0
- 去重
NaN
- 對象
new String("1")
、/a/
、{}
不去重 - 數組
[]
不去重 Symbol
不去重
代碼八(對象key)
利用瞭對象的key不可以重復的特性來進行去重
// 利用瞭對象的key不可以重復的特性來進行去重 function unique(array) { if (!Array.isArray(array)) { console.log("type error!"); return; } const obj = {}; const newArr = []; array.forEach((val) => { if (!obj[typeof val + JSON.stringify(val)]) { // 將對象序列化之後作為key來使用 obj[typeof val + JSON.stringify(val)] = 1; newArr.push(val); } }); return newArr; }
輸出:
[1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, Symbol(1), {}, []]
輸出結果說明:
- 去重
+0
、-0
、0
- 去重
NaN
- 去重對象
new String("1")
、{}
;兩個/a/
全部被刪除瞭 - 去重數組
[]
- 去重
Symbol
將不該去重的Symbol
去重瞭;將兩個/a/
全部刪除瞭
總結
方法 | 結果 | 說明 |
---|---|---|
for循環暴力解法 | [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] | 1.去重+0、-0、0; 2.NaN不去重; 3.對象new String("1")、/a/、{}不去重; 4.數組[]不去重; 5.Symbol(1)不去重; |
ES6解法 | [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], [] | 1.去重+0、-0、0; 2.去重NaN; 3.對象new String("1")、/a/、{}不去重; 4.數組[]不去重; 5.Symbol(1)不去重; |
indexOf + forEach | [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] | 1.去重+0、-0、0; 2.NaN不去重; 3.對象new String("1")、/a/、{}不去重; 4.數組[]不去重; 5.Symbol(1)不去重; |
indexOf + filter | [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] | 1.去重+0、-0、0; 2.兩個NaN都會被刪除; 3.對象new String("1")、/a/、{}不去重; 4.數組[]不去重; 5.Symbol(1)不去重; |
sort排序,不支持Symbol | [[], [], /a/, /a/, 0, "0", 0, 1, "1", String {'1'}, String {'1'}, "A", NaN, NaN, {}, {}, "a", false, "false", null, "null", true, "true", "undefined", undefined] | 1.+0、-0、0、"0"位置不同會導致去重不瞭 2.NaN不去重; 3.對象new String("1")、/a/、{}不去重; 4.數組[]不去重; 5.sort()方法不支持處理含有Symbol的數組; |
includes | [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] | 1.去重+0、-0、0; 2.去重NaN; 3.對象new String("1")、/a/、{}不去重; 4.數組[]不去重; 5.Symbol(1)不去重; |
includes+reduce | [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, String {'1'}, Symbol(1), Symbol(1), {}, {}, /a/, /a/, [], []] | 1.去重+0、-0、0; 2.去重NaN; 3.對象new String("1")、/a/、{}不去重; 4.數組[]不去重; 5.Symbol(1)不去重; |
對象key | [1, '1', 0, '0', true, false, 'true', 'false', 'a', 'A', undefined, 'undefined', null, 'null', NaN, String {'1'}, Symbol(1), {}, []] | 1.去重+0、-0、0; 2.去重NaN; 3.去重對象new String("1")、{};兩個/a/全部被刪除瞭; 4.去重數組[];5.去重Symbol |
上面隻是簡單結果的去重總結,具體的去重選擇還需要根據我們業務場景來選擇去重方法。
演示地址
可以去Github倉庫查看演示代碼
到此這篇關於JavaScript常用數組去重的方法及對比詳解的文章就介紹到這瞭,更多相關JavaScript數組去重內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 七種JS實現數組去重的方式
- js將多維數組轉為一維數組後去重排序
- javascript的數組方法大全
- JavaScript必看的10道面試題總結(推薦)
- 27個JavaScript數組常見方法匯總與實例說明