Padding zip() with undefined

北城余情 提交于 2021-01-28 08:00:08

问题


The following

Rx.Observable.zip(
     Rx.Observable.of(1,2),
     Rx.Observable.of("a"))
  .subscribe(p => console.log(p))

produces

1,a

which makes sense, but what I want it to produce is

1,a
2,undefined

I want to pad the shorter observable with undefineds until the longer one completes. Any suggestions?


回答1:


I realise adding delay can turn the .of operator async and with scan you can replace the same value with undefine

Rx.Observable.combineLatest(
 Rx.Observable.of(1,2).delay(0),
 Rx.Observable.of("a"))
 .scan((acc,curr)=>{
    acc[1]=acc[1]==curr[1]?undefined:curr[1]
    acc[0]=acc[0]==curr[0]?undefined:curr[0]
return acc
 },[])
 .subscribe(p => console.log(p))



回答2:


I think the key to this is to ensure that all of the source observables are the same length.

One solution would be to compose a counter observable that's as long as the longest source observable. It could then be concatenated to the shorter source observables, like this:

const pad = (...sources) => Rx.Observable.create(observer => {

  // Publish the source observables to make them multicast
  // and to allow the subscription order to be managed.

  const publishedSources = sources.map(source => source.publish());

  // Create an observable that emits an incremented index and
  // is as long as the longest source observable.

  const counter = Rx.Observable
    .merge(...publishedSources.map(
      source => source.map((unused, index) => index)
    ))
    .scan((max, index) => Math.max(max, index), 0)
    .distinctUntilChanged()
    .publish();

  // Zip the published sources, contatenating the counter so
  // that they are all the same length. When the counter
  // emissions are concatenated, they are mapped to undefined.

  const subscription = Rx.Observable.zip(...publishedSources.map(
    source => source.concat(counter.mapTo(undefined))
  )).subscribe(observer);

  // Connect the counter and the published sources.

  subscription.add(counter.connect());
  publishedSources.forEach(
    source => subscription.add(source.connect())
  );
  return subscription;
});

pad(
  Rx.Observable.of(1, 2),
  Rx.Observable.of("a")
).subscribe(padded => console.log(padded));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>



回答3:


My own solution is below. The idea is that all the observables are converted into endless streams, starting with the original values, enclosed in objects, and then an infinite number of undefineds. These endless streams are concatted and the result is taken as long as any of the values are original.

const unending = (obs) => 
  obs.map(value => ({value}))
     .concat(Rx.Observable
               .interval(0)
               .mapTo(undefined));

const zipPad = (...obss) => 
  Rx.Observable.zip(...obss.map(unending))
    .takeWhile(p => p.find(v => v))
    .map(p => p.map(v => v && v.value));

zipPad(Rx.Observable.of(1,2,3),
       Rx.Observable.of("a"),
       Rx.Observable.of("x", "y"))
.subscribe(p => console.log(p));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>

Anyone is welcome to improve on this answer and post their variations here.



来源:https://stackoverflow.com/questions/48103670/padding-zip-with-undefined

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!