RxJs: lossy form of zip operator

后端 未结 5 1718
一整个雨季
一整个雨季 2021-01-02 14:40

Consider using the zip operator to zip together two infinite Observables, one of which emits items twice as frequently as the other.
The current implementation is loss-l

5条回答
  •  鱼传尺愫
    2021-01-02 15:24

    I'm adding another answer for clarity, as it comes after the accepted answer (but builds on my previous answer).

    Forgive me if I've misunderstood, but I was expecting the solution to handle switching emission rates:

    then I switch between their emitting rates,

    The test supplied doesn't switch emission rate until after the first stream stops,

    Stream1: 1 2    3 4    5 6 7                 
    Stream2:     10     20    30 40 50 60 70
    

    so I've tried another test

    Stream1: 1 2      3 4     5 6
    Stream2:    10 20    30 40   50 60
    

    The test data for this stream is

    s1.next(1); s1.next(2); s2.next(10); s2.next(20); s1.next(3); s1.next(4);
    s2.next(30); s2.next(40); s1.next(5); s1.next(6);  s2.next(50); s2.next(60);
    

    From my understanding, the accepted answer fails this test.
    It outputs

    [1, 10]
    [3, 20]
    [4, 30]
    [5, 40]
    [6, 50]
    

    whereas I'd expect to see

    [1, 10]
    [3, 30]
    [5, 50]
    

    if the operator is to be symmetrical (commutative?)

    Enhancing my previous answer

    This solution is built from basic operators, so is arguably easier to understand. I can't speak to it's efficiency, perhaps will test that in another iteration.

    const s1 = new Rx.Subject();
    const s2 = new Rx.Subject();
    
    const tagged1 = s1.map(x=>[x,1])
    const tagged2 = s2.map(x=>[x,2])
    const merged = tagged1.merge(tagged2)
    const fresh = merged.scan((acc, x) => { 
        return x[1] === acc[1] ? acc : x 
      })
      .distinctUntilChanged() //fresh ones only
    const dekeyed = fresh.map(keyed => keyed[0])
    const paired = dekeyed.pairwise()
    let index = 0
    const sequenced = paired.map(x=>[x,index++])
    const alternates = sequenced.filter(x => x[1] % 2 === 0)
    const deindexed = alternates.map(x=>x[0])
    

    or in more compact form if preferred

    let index = 0
    const output = 
      s1.map(x=>[x,1]).merge(s2.map(x=>[x,2])) // key by stream id
      .scan((acc, x) => { 
        return x[1] === acc[1] ? acc : x 
      })
      .distinctUntilChanged()       //fresh ones only
      .map(keyed => keyed[0])       // de-key
      .pairwise()                   // pair
      .map(x=>[x,index++])          // add a sequence no
      .filter(x => x[1] % 2 === 0)  // take even sequence
      .map(x=>x[0])                 // deindex
    

    For testing, CodePen (refresh CodePen page after opening console, for better display)

提交回复
热议问题