开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情>>
如果有多个数组需要比对其中item的属性,找出相同的对象,应该怎么做?
在工作中,我们可能会碰到以下需求场景:
有一个列表,如果数据中的状态发生了变化,我们需要更新这一行的颜色。
最简单的方法就是,每次更新都是全量更新,将所有数据重新渲染。在数据量少的时候,可能没什么感觉,但是数据量大的时候,特别是有vdom存在的情况下,性能会很差。
因此,我们需要手动对新旧两个数组的数据进行比对,只修改状态发生了变化的数据,这样对dom的操作是最小的。性能也会大大提升。
本文重点就是要说怎么进行数组的数据比对 举个🌰:
const lista = [{name:'e',state:1},{name:'f',state:2},{name:'a',state:3},{name:'b',state:0},{name:'c',state:1},];
const listb = [{name:'a',state:1},{name:'b',state:2},{name:'c',state:3},{name:'e',state:1},{name:'f',state:2},];
// 这个例子故意将顺序打乱,是为了说明lista和listb不能通过下标将两个数组进行同步,只能通过name进行锚定。
// lista 是旧数据,listb是新数据
这种情况下我们该怎么进行比对呢? 常规方法:
// 最常规的就是两个for循环来进行比较
for(let i=0;i<listb.length;i++){
const bItem = listb[i];
for(let j=0;j<lista.length;j++){
const aItem = lista[j];
if(aItem.name === bItem.name){
aItem.state = bItem.state;
break;
}
}
}
上面的方法虽然也能达到目的,但是算法复杂度很高大概O(n^2); 下面是我推荐的密法:
// 先将listB转换成 name为key,state为value的 Map;
const listbMap = listb.reduce((m,bItem) => {
m.set(bItem.name,bItem.state);
return m;
},new Map())
lista.forEach((aItem) => {
// 兼容某些情况下查询的列表只返回有变化的数据,而不是全量返回
if(listbMap.has(aItem.name)){
aItem.state = listbMap.get(aItem.name);
}
})
一对比就可以看出来,下面的方法更简洁也更容易看懂。 而且算法复杂度也是大大降低到了O(n*2);
发两组测试数据:
方法1
lista.length=100000
listb.length=100000
耗时:4778ms
方法2
lista.length=100000
listb.length=100000
耗时:18ms
// 使用100000条数据做测试是因为数据量100W条时方法1耗时太长了,cpu有烤糊的风险。
那如果现在不是两个数组进行对比,而是m个数组进行对比呢? 第一种方法复杂度就是O(n^m); 第二种方法复杂度则是O(n*m);
还在使用第一种方法的小伙伴们赶紧收藏下吧!
PS: 使用 Map的api 比使用字面量对象性能更高。