小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
上篇,我们一起学些了去空值,生成随机数据和序列, 清空数组,数组浅拷贝等数组的高级用法,今天我们继续数组的奇妙之旅。
大家,动起来。
数组去重
非常主流的的Set配合数组去重。
var arr = ["苹果", "梨", 1,1,3, 3, undefined, {a: 1}];
var arr2 = Array.from(new Set(arr)); // ['苹果', '梨', 1, 3, undefined, {…}]
咋眼一看,没问题,666。
对于引用类型,但是这个有一个很严重的问题,就是对重的定义?
function uniqueArray(arr){
return Array.from(new Set(arr))
}
var arr = [{a:1}, {a:1}];
console.log(uniqueArray(arr)); // [{a:1}, {a:1}]
var obj1 = {a:1}
var arr2 = [obj1, obj1];
console.log(uniqueArray(arr2)); // [{a:1}]
从例子中可见看得出Array.from(new Set(arr))
对同引用 的对象是有用的,但是对属性键和值都相同的对象却没有作用的,比如你从接口获得的数据。
有些前端就要说了,这后端做就行,我微微一笑,你看迷人不?
多义真的对引用类型去重,用Set是满足不了的,这个时候,有请Array.prototype.filter
:
function uniqueArray(arr = [], key){
const keyValues = new Set();
let val;
return arr.filter(obj=> {
val = obj[key];
if(keyValues.has(val)){
return false;
}
keyValues.add(val);
return true;
})
}
var arr = [{a:1}, {a:1}];
console.log(uniqueArray(arr)); // [{a:1}]
上面的原理就是通过一个唯一的键值来过滤,不错不错,看起来不错,但是要多个值才能确认唯一的项呢?
亲, 基于上面的代码,你可以的。
求交集
网上常见的是这样:
const arr1 = [0, 1, 2]
const arr2 = [3, 2, 0]
const values = [...new Set(arr1)].filter(item =>arr2 .includes(item))
console.log(values) // [0, 2]
不通用,我们抽象一下:
function intersectSet(arr1, arr2){
return [...new Set(arr1)].filter(item =>arr2 .includes(item))
}
intersectSet(arr1, arr2) // [0, 2]
咋眼一看,也没问题,这有两个问题:
- 引用类型和相同的判断
- 性能问题 每次includes,这个是不是有点费资源。
我们改进一波, 假设对象都是有效的对象
// 引用类型
function intersect(arr1, arr2, key){
const map = new Map();
arr1.forEach(val=> map.set(val[key]))
return arr2.filter(val=> {
return map.has(val[key]);
});
}
// 原始数据类型
function intersectBase(arr1, arr2){
const map = new Map();
arr1.forEach(val=> map.set(val))
return arr2.filter(val=> {
return map.has(val);
});
}
var arr1 = [{p:0}, {p:1}, {p:2}]
var arr2 = [{p:3}, {p:2}, {p:1}]
intersect(arr1, arr2, "p"); // [{p:2, p: 1}]
const arr1 = [0, 1, 2]
const arr2 = [3, 2, 0]
intersectBase(arr1,arr2 );
这里大家可能有人会说,这性能会高一些吗?,那我们一起测试一把。
function createData(length){
return Array.from({length}, (val, i)=> {
return ~~(Math.random()* length)
})
}
var data1 = createData(10000);
var data2 = createData(10000);
console.time("intersectSet");
intersectSet(data1, data2);
console.timeEnd("intersectSet");
console.time("intersectBase");
intersectBase(data1, data2);
console.timeEnd("intersectBase");
数组长度 | intersectSet (ms) | intersectBase (ms) |
---|---|---|
1000 | 0.6 | 0.2 |
10000 | 51.2 | 2.7 |
100000 | 4973.8 | 22.1 |
可以看到,数据越多,使用Set + Array.prototype.includes
相对Map性能越差,这个是和数据结构挂钩的,平时可能不需要那么多注意,但是,这些基本知识还是要了解的。
小结
今天学习了数组去重和求交集。 今天你收获了吗?