简体   繁体   中英

Easier / more idiomatic way to do a “zip with conditional” in RXJS?

I'm trying to zip two Observables in RXJS, taking values from each in pairs when a certain condition is met. I found a way to accomplish this without using zip , but I was wondering if anybody knew of a more idiomatic way to do it.

const { zip, merge, Subject } = require("rxjs");
const rxop = require("rxjs/operators");

const a$ = new Subject();
const b$ = new Subject();

// Zipping a$ and b$ on conditional a.n === b.n
const zippedWithCondition$ = merge(
  a$.pipe(
    rxop.mergeMap(aMsg => b$.pipe(
      rxop.find(bMsg => aMsg.n === bMsg.n),
      rxop.map(bMsg => [aMsg, bMsg])
    ))
  ),
  b$.pipe(
    rxop.mergeMap(bMsg => a$.pipe(
      rxop.find(aMsg => aMsg.n === bMsg.n),
      rxop.map(aMsg => [aMsg, bMsg])
    ))
  )
);
const withConditionSub = zippedWithCondition$.subscribe(msg => {
  console.log("[ZIPPED WITH CONDITION]", msg);
});
a$.next({n: 0, type: "a"});
b$.next({n: 1, type: "b"});
a$.next({n: 1, type: "a"});
a$.next({n: 2, type: "a"});
b$.next({n: 2, type: "b"});
b$.next({n: 0, type: "b"});
a$.next({n: 3, type: "a"});
b$.next({n: 3, type: "b"});
withConditionSub.unsubscribe();

// Zipping a$ and b$ without a conditional
const normalZipped$ = zip(a$, b$);
const normalZippedSub = normalZipped$.subscribe(msg => {
  console.log("[NORMAL ZIP]", msg);
});
a$.next({n: 0, type: "a"}); // same order as above
b$.next({n: 1, type: "b"});
a$.next({n: 1, type: "a"});
a$.next({n: 2, type: "a"});
b$.next({n: 2, type: "b"});
b$.next({n: 0, type: "b"});
a$.next({n: 3, type: "a"});
b$.next({n: 3, type: "b"});
normalZippedSub.unsubscribe();

Output:

[ZIPPED WITH CONDITION] [ { n: 1, type: 'a' }, { n: 1, type: 'b' } ]
[ZIPPED WITH CONDITION] [ { n: 2, type: 'a' }, { n: 2, type: 'b' } ]
[ZIPPED WITH CONDITION] [ { n: 0, type: 'a' }, { n: 0, type: 'b' } ]
[ZIPPED WITH CONDITION] [ { n: 3, type: 'a' }, { n: 3, type: 'b' } ]

[NORMAL ZIP] [ { n: 0, type: 'a' }, { n: 1, type: 'b' } ]
[NORMAL ZIP] [ { n: 1, type: 'a' }, { n: 2, type: 'b' } ]
[NORMAL ZIP] [ { n: 2, type: 'a' }, { n: 0, type: 'b' } ]
[NORMAL ZIP] [ { n: 3, type: 'a' }, { n: 3, type: 'b' } ]

See how for my [ZIPPED WITH CONDITION] the n 's match for each pair. For normal zip, that doesn't occur, because messages came in out-of-order.

So is there a better way to do this? Maybe in a way that is extensible to zipping any number of observables on a condition?

In your solution you are building 2n + 2m observables. In other words, if a$ emits m values and b$ emits n values, you are creating 2n + 2m observables.

To see this, you just need to add complete methods to a$ and b$ to your test data sequence, like in the following example

a$.next({n: 0, type: "a"});
b$.next({n: 1, type: "b"});
a$.next({n: 1, type: "a"});
a$.next({n: 2, type: "a"});
b$.next({n: 2, type: "b"});
b$.next({n: 0, type: "b"});
a$.next({n: 3, type: "a"});
b$.next({n: 3, type: "b"});
a$.complete();
b$.complete();
withConditionSub.unsubscribe();

I think that, in order to find a good solution, you should complete you requirements considering also the time variable. For instance: " a$ and b$ emit always objects with n property increased by 1 sequentially - no holes in the sequential sequence are foreseen - and so on and so for.

If time related requirements can not be specified, it is difficult to imagine an RxJS solution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM