數組去重,這是一個老梗了...今天我又拿出來講了... 咱們在考慮全面一點的狀況下,數組去重的實現,好比針對NaN
,undefined
,{}
; 這其中涉及的知識點挺多,不信跟着走一波; 這裏不考慮瀏覽器兼容性這些破問題,由於涉及ES5&6javascript
只包含一些能夠直接比較的數值java
測試數組node
[1,1,'','','e','e',true,'true',true,false,false,'false',undefined,'undefined',undefined,null,'null',null]面試
for
方法function uniqueUseFor(array) {
var temp = []; //一個臨時數組
//遍歷當前數組
for (var i = 0, j = array.length; i < j; i++) {
//很直白,新數組內判斷是否有這個值,沒有的狀況下,就推入該新數組
temp.indexOf(array[i]) === -1 ? temp.push(array[i]) : '';
}
return temp;
}
// result: [1, "", "e", true, "true", false, "false", undefined, "undefined", "null", null]
複製代碼
forEach
方法function uniqueUseForEach(array) {
// 傳入值必須存在,且長度小於等於1的時候直接返回數組
if (array && array.length <= 1) {
return array;
} else {
var temp = []; //一個臨時數組
//遍歷當前數組
array.forEach(function (value, index) {
temp.indexOf(value) == -1 ? temp.push(value) : '';
})
return temp;
}
}
// result: [1, "", "e", true, "true", false, "false", undefined, "undefined", "null", null]
複製代碼
for-of
方法function uniqueUseForOf(array) {
const temp = []; //一個臨時數組
// 傳入值必須存在,且長度小於等於1的時候直接返回數組
if (array && array.length <= 1) {
return array;
} else {
//遍歷當前數組
for (let x of array) {
temp.indexOf(x) === -1 ? temp.push(x) : '';
}
}
return temp;
}
// result: [1, "", "e", true, "true", false, "false", undefined, "undefined", "null", null]
複製代碼
包含NaN
,undefined
,null
數組
測試數組瀏覽器
[1,1,'true',true,true,5,'F',false, undefined, null,null,undefined, NaN, 0, 1, 'a', 'a', NaN,'NaN']測試
知識點ui
NaN有兩中通用斷定方法和數組中一種斷定方法:spa
一個是絕對不全等於(===
)自身prototype
一個是ES6
的isNaN()
數組原型鏈上的Array.prototype.includes()
function uniqueUseNotAllEqual(array) {
var temp = [], //一個臨時數組
mark = true; // 標識位
//遍歷當前數組
for (var i = 0, j = array.length; i < j; i++) {
// 標識位的做用就是用來判斷是否存在NaN,第一次找到保留到新數組中
// 而後標識位置改成false是爲了再次找到的時候不推入數組
if (array[i] !== array[i]) {
// 這裏的不等特性,也能夠用isNaN判斷[ES6]
mark && temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';
mark = false;
} else
temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';
}
return temp;
}
// result: [1, "true", true, 5, "F", false, undefined, null, NaN, 0, "a", "NaN"]
複製代碼
Array.prototype.includes()
大法function uniqueCompareUseIncludes(array) {
// 傳入值必須存在,且長度小於等於1的時候直接返回數組
if (array && array.length <= 1) {
return array;
} else {
let temp = []; //一個臨時數組
//遍歷當前數組
for (let x of array) {
// includes() 方法用來判斷當前數組是否包含某指定的值,若是是,則返回 true,不然返回 false。
temp.includes(x) ? '': temp.push(x) ;
}
return temp;
}
}
// result: [1, "true", true, 5, "F", false, undefined, null, NaN, 0, "a", "NaN"]
複製代碼
Array.from
或拓展運算符[...
]結合Set
大法知識點
Set
的值具備惟一性,內部會自動===
比較,是可迭代對象(iterable),有點特殊的是NaN
這貨雖然有不全等的特性,在Set
裏面認爲是相同的,因此只能有一個Array.from
和...
能夠把相似數組【nodelist or arguments】這類可迭代的對象中轉爲一個標準的數組// Array.from + Set的方法
Array.from(new Set([1,1,'true',true,true,5,'F',false, undefined, null,null,undefined, NaN, 0, 1, 'a', 'a', NaN,'NaN']))
// resule: [1, "true", true, 5, "F", false, undefined, null, NaN, 0, "a", "NaN"]
// ... + Set的方法
[...new Set([1,1,'true',true,true,5,'F',false, undefined, null,null,undefined, NaN, 0, 1, 'a', 'a', NaN,'NaN'])]
//result: [1, "true", true, 5, "F", false, undefined, null, NaN, 0, "a", "NaN"]
複製代碼
包含{}
,NaN
,undefined
,null
測試數組
[1,1,'true',true,true,5,'F',false, undefined, null,null,undefined, NaN,{},{},'{}', 0, 1, 'a', 'a', NaN]
知識點
{}
的比較真心很差作,有殘缺性的比較能夠這樣寫 JSON.stringify({}) == '{}'
一個比較完美的方案是藉助for in
結合原型鏈的toString
來判斷
ES5for-in
+ call
+ for
方案
function uniqueUseForIn(array) {
var temp = [];
var emptyObjectMark = true; // 標識位
var NaNObjectMark = true; // 標識位
// 判斷空對象,這塊判斷折騰了許久
function isEmptyObject(object) {
if (Object.prototype.toString.call(object) === "[object Object]") {
for (var i in object) {
// 存在屬性或方法,則不是空對象
return false
}
return true;
} else {
return false;
}
}
// 傳入值必須存在,且長度小於等於1的時候直接返回數組
if (array && array.length <= 1) {
return array;
} else {
//遍歷當前數組
for (var i = 0, j = array.length; i < j; i++) {
// 標識位的做用就是用來判斷是否存在NaN和空對象,第一次找到保留到新數組中
// 而後標識位置改成false是爲了再次找到的時候不推入數組
if (isEmptyObject(array[i])) {
emptyObjectMark && temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';
emptyObjectMark = false;
} else if (array[i] !== array[i]) {
NaNObjectMark && temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';
NaNObjectMark = false;
} else
temp.indexOf(array[i]) == -1 ? temp.push(array[i]) : '';
}
return temp;
}
}
// result:[1, "true", true, 5, "F", false, undefined, null, NaN, Object, "{}", 0, "a"]
複製代碼
多維數組扁平化再去重;
迴應:
留言板的小夥伴說去重深度不夠。。2017/5/12
測試數組
[1, 1, [['true', true, true, [5, 'F'], false], undefined], null, null, [undefined, NaN, {}], {}, '{}', 0,1, 'a','a', NaN]
知識點
for-in
+ call
+ for
+ 遞歸扁平化方案function uniqueArrayWithFlattern(array) {
var _array = array || []; // 保存傳參
// 判斷空對象,這塊判斷折騰了許久
function isEmptyObject(object) {
if (Object.prototype.toString.call(object) === "[object Object]") {
for (var i in object) {
// 存在屬性或方法,則不是空對象
return false
}
return true;
} else {
return false;
}
}
// 遍歷查詢判斷,扁平化數組
function forArrayFlattern(arr) {
for (var m = 0, n = arr.length; m < n; m++) {
if (Array.isArray(arr[m])) {
var _tempSpliceArr = _array.splice(m, 1)[0];
_array = _array.concat(_tempSpliceArr);
return forArrayFlattern(_array);
}
}
return uniqueArr(_array);
}
// 傳入值必須存在,且長度小於等於1的時候直接返回數組
if (_array && _array.length <= 1) {
return _array
} else {
if (Array.isArray(_array)) {
return forArrayFlattern(_array)
} else {
return _array;
}
}
// 數組去重
function uniqueArr(_array) {
var temp = [];
var emptyObjectMark = true; // 標識位
var NaNObjectMark = true; // 標識位
console.log(_array.length)
//遍歷當前數組
for (var a = 0, b = _array.length; a < b; a++) {
// 標識位的做用就是用來判斷是否存在NaN和空對象,第一次找到保留到新數組中
// 而後標識位置改成false是爲了再次找到的時候不推入數組
console.log(_array[a]);
if (isEmptyObject(_array[a])) {
emptyObjectMark && temp.indexOf(_array[a]) == -1 ? temp.push(_array[a]) : '';
emptyObjectMark = false;
} else if (_array[a] !== _array[a]) {
NaNObjectMark && temp.indexOf(_array[a]) == -1 ? temp.push(_array[a]) : '';
NaNObjectMark = false;
} else {
temp.indexOf(_array[a]) === -1 ? temp.push(_array[a]) : '';
}
}
console.log(temp);
return temp;
}
}
// result:[1, null, Object, "{}", 0, "a", NaN, undefined, "true", true, false, 5, "F"]
// 用ES6來寫的話,應該代碼量能夠稍微再精簡些
複製代碼
相信各位小夥伴把這個弄懂了以後,各類面試中的花樣數組去重要求對你來講都不是很坑了;