学习Javascript之数组去重

·  阅读 1648

前言

• 公众号：「前端进阶学习」，回复「666」，获取一揽子前端技术书籍

正文

``````var meta = [
0,
'0',
true,
false,
'true',
'false',
null,
undefined,
Infinity,
{},
[],
function(){},
{ a: 1, b: 2 },
{ b: 2, a: 1 },
];
var meta2 = [
NaN,
NaN,
Infinity,
{},
[],
function(){},
{ a: 1, b: 2 },
{ b: 2, a: 1 },
];
var sourceArr = [...meta, ... Array(1000000)
.fill({})
.map(() => meta[Math.floor(Math.random() * meta.length)]),
...meta2];

``````// 长度为14的数组
[false, "true", Infinity, true, 0, [], {}, "false", "0", null, undefined, {a: 1, b: 2}, NaN, function(){}]

基础数据类型

1. ES6中Set

``````console.time('ES6中Set耗时：');
var res = [...new Set(sourceArr)];
console.timeEnd('ES6中Set耗时：');
// ES6中Set耗时：: 28.736328125ms
console.log(res);
// 打印数组长度20： [false, "true", Infinity, true, 0, [],  [], {b: 2, a: 1}, {b: 2, a: 1}, {}, {}, "false", "0", null, undefined, {a: 1, b: 2}, {a: 1, b: 2}, NaN, function(){}, function(){}]

``````console.time('ES6中Set耗时：');
var res = Array.from(new Set(sourceArr));
console.timeEnd('ES6中Set耗时：');
// ES6中Set耗时：: 28.538818359375ms
console.log(res);
// 打印数组长度20：[false, "true", Infinity, true, 0, [],  [], {b: 2, a: 1}, {b: 2, a: 1}, {}, {}, "false", "0", null, undefined, {a: 1, b: 2}, {a: 1, b: 2}, NaN, function(){}, function(){}]

**优点：**简洁方便，可以区分`NaN`

**缺点：**无法识别相同对象和数组；

2. 使用indexOf

``````function unique(arr) {
if (!Array.isArray(arr)) return;
var result = [];
for (var i = 0; i < arr.length; i++) {
if (array.indexOf(arr[i]) === -1) {
result.push(arr[i])
}
}
return result;
}
console.time('indexOf方法耗时：');
var res = unique(sourceArr);
console.timeEnd('indexOf方法耗时：');
// indexOf方法耗时：: 23.376953125ms
console.log(res);
// 打印数组长度21: [false, "true", Infinity, true, 0, [],  [], {b: 2, a: 1}, {b: 2, a: 1}, {}, {}, "false", "0", null, undefined, {a: 1, b: 2}, {a: 1, b: 2}, NaN,NaN, function(){}, function(){}]

3. 使用includes方法

`indexOf`类似，但`includes`是ES7(ES2016)新增API：

``````function unique(arr) {
if (!Array.isArray(arr)) return;
var result = [];
for (var i = 0; i < arr.length; i++) {
if (!result.includes(arr[i])) {
result.push(arr[i])
}
}
return result;
}
console.time('includes方法耗时：');
var res = unique(sourceArr);
console.timeEnd('includes方法耗时：');
// includes方法耗时：: 32.412841796875ms
console.log(res);
// 打印数组长度20：[false, "true", Infinity, true, 0, [],  [], {b: 2, a: 1}, {b: 2, a: 1}, {}, {}, "false", "0", null, undefined, {a: 1, b: 2}, {a: 1, b: 2}, NaN, function(){}, function(){}]

4. 使用filter和indexOf方法

``````function unique(arr) {
if (!Array.isArray(arr)) return;
return arr.filter(function(item, index, arr) {
//当前元素，在原始数组中的第一个索引==当前索引值，否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
console.time('filter和indexOf方法耗时：');
var res = unique(sourceArr);
console.timeEnd('filter和indexOf方法耗时：');
// includes方法耗时：: 24.135009765625ms
console.log(res);
// 打印数组长度19：[false, "true", Infinity, true, 0, [],  [], {b: 2, a: 1}, {b: 2, a: 1}, {}, {}, "false", "0", null, undefined, {a: 1, b: 2}, {a: 1, b: 2}, function(){}, function(){}]

5. 利用reduce+includes

``````var unique = (arr) =>  {
if (!Array.isArray(arr)) return;
return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
var res = unique(sourceArr);
console.time('reduce和includes方法耗时：');
var res = unique(sourceArr);
console.timeEnd('reduce和includes方法耗时：');
// reduce和includes方法耗时：: 100.47802734375ms
console.log(res);
// 打印数组长度20：[false, "true", Infinity, true, 0, [],  [], {b: 2, a: 1}, {b: 2, a: 1}, {}, {}, "false", "0", null, undefined, {a: 1, b: 2}, {a: 1, b: 2}, NaN, function(){}, function(){}]

6. 利用Map结构

``````function unique(arr) {
if (!Array.isArray(arr)) return;
let map = new Map();
let result = [];
for (let i = 0; i < arr.length; i++) {
if(map .has(arr[i])) {
map.set(arr[i], true);
} else {
map.set(arr[i], false);
result.push(arr[i]);
}
}
return result;
}
console.time('Map结构耗时：');
var res = unique(sourceArr);
console.timeEnd('Map结构耗时：');
// Map结构耗时：: 41.483154296875ms
console.log(res);
// 打印数组长度20：[false, "true", Infinity, true, 0, [],  [], {b: 2, a: 1}, {b: 2, a: 1}, {}, {}, "false", "0", null, undefined, {a: 1, b: 2}, {a: 1, b: 2}, NaN, function(){}, function(){}]

7. 双层嵌套，使用splice删除重复元素

``````function unique(arr){
if (!Array.isArray(arr)) return;
for(var i = 0; i < arr.length; i++) {
for(var j = i + 1; j<  arr.length; j++) {
if(Object.is(arr[i], arr[j])) {// 第一个等同于第二个，splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
console.time('双层嵌套方法耗时：');
var res = unique(sourceArr);
console.timeEnd('双层嵌套方法耗时：');
// 双层嵌套方法耗时：: 41500.452880859375ms
console.log(res);
// 打印数组长度20: [false, "true", Infinity, true, 0, [],  [], {b: 2, a: 1}, {b: 2, a: 1}, {}, {}, "false", "0", null, undefined, {a: 1, b: 2}, {a: 1, b: 2}, NaN, function(){}, function(){}]

8. 利用sort方法

`````` function unique(arr) {
if (!Array.isArray(arr)) return;
arr = arr.sort((a, b) => a - b);
var result = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
result.push(arr[i]);
}
}
return result;
}
console.time('sort方法耗时：');
var res = unique(sourceArr);
console.timeEnd('sort方法耗时：');
// sort方法耗时：: 936.071044921875ms
console.log(res);
// 数组长度357770，剩余部分省略
// 打印：(357770) [Array(0), Array(0), 0...]

Object

9. 利用hasOwnProperty和filter

``````function unique(arr) {
if (!Array.isArray(arr)) return;
var obj = {};
return arr.filter(function(item, index, arr) {
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
console.time('hasOwnProperty方法耗时：');
var res = unique(sourceArr);
console.timeEnd('hasOwnProperty方法耗时：');
// hasOwnProperty方法耗时：: 258.528076171875ms
console.log(res);
// 打印数组长度13: [false, "true", Infinity, true, 0, [], {}, "false", "0", null, undefined, NaN, function(){}]

10. 利用对象key不重复的特性

``````function unique(arr) {
if (!Array.isArray(arr)) return;
var result = [];
var  obj = {};
for (var i = 0; i < arr.length; i++) {
var key = typeof arr[i] + JSON.stringify(arr[i]) + arr[i];
if (!obj[key]) {
result.push(arr[i]);
obj[key] = 1;
} else {
obj[key]++;
}
}
return result;
}
console.time('对象方法耗时：');
var res = unique(sourceArr);
console.timeEnd('对象方法耗时：');
// 对象方法耗时：: 585.744873046875ms
console.log(res);
// 打印数组长度15: [false, "true", Infinity, true, 0, [], {b: 2, a: 1}, {}, "false", "0", null, undefined, {a: 1, b: 2}, NaN, function(){}]

``````function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
function unique(arr) {
if (!Array.isArray(arr)) return;
var result = [];
var  obj = {};
for (var i = 0; i < arr.length; i++) {
// 此处加入对象和数组的判断
if (Array.isArray(arr[i])) {
arr[i] = arr[i].sort((a, b) => a - b);
}
if (isObject(arr[i])) {
let newObj = {}
Object.keys(arr[i]).sort().map(key => {
newObj[key]= arr[i][key];
});
arr[i] = newObj;
}
var key = typeof arr[i] + JSON.stringify(arr[i]) + arr[i];
if (!obj[key]) {
result.push(arr[i]);
obj[key] = 1;
} else {
obj[key]++;
}
}
return result;
}
console.time('对象方法耗时：');
var res = unique(sourceArr);
console.timeEnd('对象方法耗时：');
// 对象方法耗时：: 793.142822265625ms
console.log(res);
// 打印数组长度14: [false, "true", Infinity, true, 0, [], {b: 2, a: 1}, {}, "false", "0", null, undefined, NaN, function(){}]

结论

ES6中Set简单优雅，速度快基础类型推荐使用。版本要求高，不支持对象数组和`NaN`