一个司空见惯的问题——JS去重。
JavaScript数组去重是司空见惯的一件事情了,是一个无法忽视的问题,网站建设中或多或少会涉及到这些问题,所以深入浅出的说一下数组去重。
基础版-只包含一些可以直接比较的数值
测试数组
[1,1,”,”,’e’,’e’,true,’true’,true,false,false,’false’,undefined,’undefined’,undefined,null,’null’,null]
[ES5]万能的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]
[ES5]内置的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]
[ES6]内置的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’]
知识点
NaN有两中通用判定方法和数组中一种判定方法:
一个是绝对不全等于(===)自身
一个是ES6的isNaN()
数组原型链上的Array.prototype.includes()
[ES5]: 不等特性,需要借助占位符
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"]
[ES6]内置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"]
[ES6] 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来判断
[ES5]for-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"]
基础版-只包含一些可以直接比较的数值
测试数组
[1,1,”,”,’e’,’e’,true,’true’,true,false,false,’false’,undefined,’undefined’,undefined,null,’null’,null]
[ES5]万能的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]
[ES5]内置的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]
[ES6]内置的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’]
知识点
NaN有两中通用判定方法和数组中一种判定方法:
一个是绝对不全等于(===)自身
一个是ES6的isNaN()
数组原型链上的Array.prototype.includes()
[ES5]: 不等特性,需要借助占位符
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"]
[ES6]内置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"]
[ES6] 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来判断
[ES5]for-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"]