rxjs - 根据对象的可观察对象过滤可观察对象数组
问题描述
我有一个Observable<Carrier[]>
call carriers$
,这很少发出。每个Carrier
都有它的BehaviorSubject
,无论它是否在购物车中,它都会保留。
这是尽可能简化的
interface Carreir {
id: number;
inCart: BehaviorSubject<boolean>;
}
export class AppService {
carriers$: Observable<Carreir[]>; // Let's ignore creation
// Take all carriers and filter only those that are inCart
inCart: Observable<Carreir[]> = this.carriers$.pipe(
// I tried concatMap, mergeMap, flatMap, exaustMap, but
// This, unfortunately, does not use the Observable part of it.
map(carriers => carriers.filter(carrier => carrier.inCart.value)),
);
}
编辑1:
BehaviorSubject
in的原因Carrier
是为了让carriers$
observable 不需要刷新过滤、排序等计算量比较大的任务。由Carrier[]
3000 多个元素组成。被过滤掉的元素仍然可以成为购物车的一部分(例如:过滤一个城市,将一些添加到购物车,过滤另一个城市并从另一个城市添加更多)。
编辑 2: https ://stackblitz.com/edit/angular-ivy-h4tk8e?file=src%2Fapp%2Fapp.component.ts
解决方案
如果我正确理解您的问题,您想要的是过滤购物车中的所有运营商并通过 Observable 发出它们inCart$
。载体在放入购物车时true
通过 Subject 发射,从购物车中取出时发射。inCart
false
您从carriers$
它开始发出一组运营商。
现在,一个可能的解决方案可能如下所示:
首先,您转换carriers$
为布尔值流,这些值是由inCart
所有 Carriers 的属性中保存的所有 BehaviorSubjects 发出的值。这可以这样做
inCartBools$ = this.carriers$.pipe(
// any time carriers$ emit we create an array of BehaviorSubject<bool>, one per Carrier
switchMap(carriers => carriers.map(c => c.inCart)),
// then we flatten the Observables to obtains a stream of booleans
mergeMap(d => d),
);
但是一个布尔值流不携带任何关于发出真假的 Carrier 的信息,所以我们需要稍微调整一下代码,以便我们获得一个包含 Carrier 的 id 和值的对象流布尔值(以便我们知道 Carrier 是否已添加或删除)。这可以像这样完成
inCartBoolsAndCarrierIds$ = this.carriers$.pipe(
switchMap(carriers => carriers.map(c => c.inCart.pipe(
// rather than just a simple bool we return an object containing also the Carrier id
map(v => ({id: c.id, v}))
))),
mergeMap(d => d),
);
任何时候 inCartBoolsAndCarrierIds$ 发出,我们都需要将 Carrier id 添加到购物车或将其删除,具体取决于 bool 的值。我们还需要保留我们存储添加的 id 的数组的内存。这可以使用这样的scan
运算符来完成
inCart$ = this.carriers$.pipe(
switchMap(carriers => carriers.map(c => c.inCart.pipe(map(v => ({id: c.id, v}))))),
mergeMap(d => d),
// we accumulate the ids of the Carriers in the cart via the scan operator, using a dictionary to store such ids
scan((acc, val) => {
if (val.v) {
acc[val.id] = {id: val.id}
} else {
delete acc[val.id]
}
return acc
}, {} as {[k: number]: {id: number}}),
// eventually we return only the values of the ids
map(dict => Object.values(dict))
);
最终代码可以在这个 stackblitz中检查。
作为解决方案,它看起来有点复杂,但是,如果我正确理解了您的问题,这是一个可行的解决方案。
推荐阅读
- mysql - 如何最好地刷新 EC2 中的暂存实例
- vb.net - 删除文本文件内容中的双引号
- javascript - Puppeteer 从外部 .js 文件调用 javascript 函数
- vba - CreateObject("ADODB.Command") - 运行时错误“429”:ActiveX 组件无法创建对象
- gradle - Gradle中相同依赖项的多个版本冲突
- javascript - 如何从脏形式的对象中获取值并放入数组中?
- python - 读取字符串并减少它们
- java - Android Studio Java JSON 获取数据问题
- java - Java 应用程序无法处理每周四晚上传入的特殊文件,而 Cassandra 抛出错误
- mysql - 我在 Mysql 中同时处理购买的方式是否正确?