文章目录
总结
去重方法 | 10w数据耗时ms | 复杂数据去重效果 |
---|---|---|
双重循环 | 3139 | 普通类型除了 NaN,引用数据类型不能 |
Array.prototype.indexOf() | 2784 | 普通类型除了 NaN,引用数据类型不能 |
Array.prototype.sort() | 53 | 普通类型除了 NaN,引用数据类型不能 |
Array.prototype.includes() | 3018 | 排序结果错误,相邻判断效果不好 |
Array.prototype.reduce() | 6 | 普通数据类型都可以,引用数据类型不能 |
对象键值对(普通数据类型+特殊数据) | 60 | 排序结果错误,相邻判断效果不好 |
对象键值对(复杂对象) | 77 | 去重正确 |
Map | 9 | 普通数据类型都可以,引用数据类型不能 |
set | 4 | 普通数据类型都可以,引用数据类型不能 |
具体使用看情况而定
includes\Map\Set\Object的属性名是可以区分 NaN
的
测试数据
- 10w 数据
const arr = [];
// 生成[0, 100000]之间的随机数
for (let i = 0; i < 100000; i++) {
arr.push(0 + Math.floor((100000 - 0 + 1) * Math.random()))
}
- 复杂数据
const arr = [1, 1, '1', '1', 0, 0, '0', '0', undefined, undefined, null, null, NaN, NaN, {}, {}, [], [], /a/, /a/];
双重循环
Array.prototype.unique = function () {
const newArray = [];
for (let i = 0; i < this.length; i++) {
for (let j = i + 1; j < this.length; j++) {
if (this[i] === this[j]) {
j = ++i;
}
}
newArray.push(this[i]);
}
return newArray;
}
- 10w数组时间:3139.086181640625ms
- 复杂情况结果:NaN Object Array 正则都无法区分,全等无法区分引用数据类型和 NaN
Array.prototype.indexOf()
基本思路:如果索引不是第一个索引,说明是重复值。
Array.prototype.unique = function () {
const newArray = [];
this.forEach(item => {
if (newArray.indexOf(item) === -1) {
newArray.push(item);
}
});
return newArray;
}
- 10w数组时间:2784.8837890625ms
- 复杂情况结果:同上,全等无法区分引用数据类型和 NaN
Array.prototype.sort()
基本思路:先对原数组进行排序,然后再进行元素比较。
Array.prototype.unique = function () {
const newArray = [];
this.sort();
for (let i = 0; i < this.length; i++) {
if (this[i] !== newArray[newArray.length - 1]) {
newArray.push(this[i]);
}
}
return newArray;
}
- 10w数组时间:53.389892578125ms
- 复杂数据结果:排序有问题,且全等无法判断引用数据类型和 NaN
Array.prototype.includes()
语法:arr.includes(valueToFind[, fromIndex])
Array.prototype.unique = function () {
const newArray = [];
this.forEach(item => {
if (!newArray.includes(item)) {
newArray.push(item);
}
});
return newArray;
}
- 10w数据时间:3018.489013671875ms
- 复杂数据效果:可以区分所有基础数据类型,引用类型不行。
polyfill
f (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(valueToFind, fromIndex) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(valueToFind, elementK) is true, return true.
if (sameValueZero(o[k], valueToFind)) {
return true;
}
// c. Increase k by 1.
k++;
}
// 8. Return false
return false;
}
});
}
Array.prototype.reduce()
Array.prototype.unique = function () {
return this.sort().reduce((init, current) => {
if(init.length === 0 || init[init.length - 1] !== current){
init.push(current);
}
return init;
}, []);
}
- 10w数据时间:6ms
- 复杂数据效果:排序首先就错误了
对象键值对
基本思路:利用了对象的key不可以重复的特性来进行去重。
需要注意:
- 无法区分隐式类型转换成字符串后一样的值,比如 1 和 ‘1’
- 无法处理复杂数据类型,比如对象(因为对象作为 key 会变成 [object Object])
- 特殊数据,比如 ‘proto’,因为对象的 proto 属性无法被重写
解决问题(1,3)
Array.prototype.unique = function () {
const newArray = [];
const tmp = {};
for (let i = 0; i < this.length; i++) {
if (!tmp[typeof this[i] + this[i]]) {
tmp[typeof this[i] + this[i]] = 1;
newArray.push(this[i]);
}
}
return newArray;
}
- 10w数据时间:60.39306640625ms
- 复杂数据效果:效果较好,因为键值为类型+字符串,相同内容不同引用的对象转换成字符串是一样的。
tmp:
解决问题(2)
Array.prototype.unique = function () {
const newArray = [];
const tmp = {};
for (let i = 0; i < this.length; i++) {
// 使用JSON.stringify()进行序列化
if (!tmp[typeof this[i] + JSON.stringify(this[i])]) {
// 将对象序列化之后作为key来使用
tmp[typeof this[i] + JSON.stringify(this[i])] = 1;
newArray.push(this[i]);
}
}
return newArray;
}
tmp:
- 10w数据时间:77.2470703125ms
- 复杂数据效果:对象序列化时,正则表达式结果为{},与空对象重复。
Map
键的比较是基于 SameValueZero 算法:NaN 和 NaN 相等,剩下其他的值是根据===运算符的结果判断是否相等。
Array.prototype.unique = function () {
const tmp = new Map();
return this.filter(item => {
return !tmp.has(item) && tmp.set(item, 1);
})
}
- 10w数据时间:9.539794921875ms
- 复杂数据效果:普通数据类型都可以,复杂数据类型不行
Set
set 中 NaN 之间被视为相同的值。
Array.prototype.unique = function () {
return [...new Set(this)];
}
- 10w数据时间:4.939208984375ms
- 数据去重效果:普通数据类型可以,复杂数据类型不行