Rxjs 基于对象的可观测值过滤对象的可观测阵列

Rxjs 基于对象的可观测值过滤对象的可观测阵列,rxjs,Rxjs,我有一个名为carriers$的可观察的,它很少发射。每个承运人都有自己的行为主体,无论是否在购物车中,它都会持有 这是尽可能简化的 interface Carreir { id: number; inCart: BehaviorSubject<boolean>; } export class AppService { carriers$: Observable<Carreir[]>; // Let's ignore creation // Take

我有一个名为
carriers$
可观察的
,它很少发射。每个
承运人
都有自己的
行为主体
,无论是否在购物车中,它都会持有

这是尽可能简化的

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)),
  );
}
接口载体{
id:编号;
化身:行为主体;
}
导出类应用程序服务{
carriers$:Observable;//让我们忽略创建
//采取所有载体,只过滤那些载体
化身:可观察=此.carriers$.pipe(
//我试过concatMap、mergeMap、flatMap、exaustMap,但是
//不幸的是,这并没有使用它的可观察部分。
map(carriers=>carriers.filter(carrier=>carrier.incontrat.value)),
);
}

编辑1:

Carrier
中的
BehaviorSubject
的原因是
carriers$
observable不需要刷新过滤、排序和其他计算相对繁重的任务。
Carrier[]
由3000多个元素组成。过滤掉的元素仍然可以是购物车的一部分(例如:过滤一个城市,将一些添加到购物车,过滤另一个城市,并从另一个城市添加更多)

编辑2:
如果没有有效的stackblitz,就很难为您的需求编写准确的代码。但这就是我所拥有的,使用任务和完成的任务(而不是载体和化身载体)

我这样定义接口:

export interface ToDo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}
请注意,
complete
定义为
boolean
,而不是
BehaviorSubject

为用户检索TODO的代码如下(这可能与您无关)

注意,这将获取检索到的TODO列表,映射数组,然后过滤数组中的每个元素。这与您的代码非常相似

你可以在这里找到一个有效的stackblitz:


如果这不能准确回答您的问题,请随时进行“闪电战”,并进行调整,以更好地反映您的问题。

如果我正确理解您的问题,您需要的是过滤购物车中的所有载体,并通过可观察的
化身$
发射它们。当承运人被放置在购物车中时,他们通过
incontit
主题发出
true
,当他们从购物车中移除时,他们发出
false

您从发射载波阵列的
carriers$
开始

现在,可能的解决方案如下所示:

export interface ToDo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}
首先,将
carriers$
转换为布尔值流,布尔值是所有carriers的
incontain
属性中保存的所有行为子对象发出的值。 可以这样做

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),
  );
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),
  );
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))
  );
任何时候,只要InjectBoolsandCarrierids$发出,我们就需要将Carrier id添加到购物车中,或者将其删除,具体取决于bool的值。我们还需要保留存储添加ID的数组的内存。这可以使用
scan
操作符完成,如下所示

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),
  );
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),
  );
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))
  );
最后的代码可以签入


作为解决方案,它看起来有点复杂,但如果我正确理解了您的问题,它是一个可行的解决方案。

从界面上看,每个运营商都有一个标志,表明该运营商是否在购物车中。(我不确定为什么它是BehaviorSubject而不是简单的布尔值…我以前没有见过这样定义的接口。)并且不建议以这种方式使用BehaviorSubject的
.value
属性。你能提供更多关于你想要完成的事情吗?也许是一场闪电战来证明你的问题?我们很乐意提供帮助。@DeborahK在
Carrier
中设置
BehaviorSubject
的原因是
carriers$
observable不需要刷新过滤、排序和其他相对计算繁重的任务。您能详细说明一下吗?这有什么帮助?为了防止对可观察对象进行重新筛选/排序,您可以使用
shareReplay(1)
@DeborahK该
Inborat
实际上从未用于筛选,我有3000多个载体,所有载体都必须与类型、地址等匹配。如果非核心值发生变化,我不想重新筛选…我实际上想得到
载体而不是它的ID,但这很容易解决,谢谢。您认为这是解决这个问题的正确方法吗?您的实现是非常被动和无状态的,从这个意义上讲,您的组件中没有存储任何状态,您只使用可观察的流。同时,您为每个运营商创建一个行为主体,我不知道这是否会对您的性能和内存需求产生影响。您还可以选择更具状态的实现,如in(请检查正确性,因为我写得很快)。还应检查性能是否存在差异。