数组的高级用法 (2)

270 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

上篇,我们一起学些了去空值,生成随机数据和序列, 清空数组,数组浅拷贝等数组的高级用法,今天我们继续数组的奇妙之旅。

大家,动起来。

数组去重

非常主流的的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]

咋眼一看,也没问题,这有两个问题:

  1. 引用类型和相同的判断
  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)
10000.60.2
1000051.22.7
1000004973.822.1

可以看到,数据越多,使用Set + Array.prototype.includes相对Map性能越差,这个是和数据结构挂钩的,平时可能不需要那么多注意,但是,这些基本知识还是要了解的。

小结

今天学习了数组去重和求交集。 今天你收获了吗?