Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJS takeWhile but include the last value

I have a RxJS5 pipeline looks like this

Rx.Observable.from([2, 3, 4, 5, 6])   .takeWhile((v) => { v !== 4 }) 

I want to keep the subscription until I see 4, but I want to last element 4 also to be included in the result. So the example above should be

2, 3, 4 

However, according to official document, takeWhile operator is not inclusive. Which means when it encounters the element which doesn't match predicate we gave, it completes the stream immediately without the last element. As a result, the above code will actually output

2, 3 

So my question is, what's the easiest way I can achieve takeWhile but also emit the last element with RxJS?

like image 741
Fang-Pen Lin Avatar asked Jun 19 '17 23:06

Fang-Pen Lin


2 Answers

Since RxJS 6.4.0 this is now possible with takeWhile(predicate, true).

There's already an opened PR that adds an optional inclusive parameter to takeWhile: https://github.com/ReactiveX/rxjs/pull/4115

There're at least two possible workarounds:

  1. using concatMap():

    of('red', 'blue', 'green', 'orange').pipe(   concatMap(color => {     if (color === 'green') {       return of(color, null);     }     return of(color);   }),   takeWhile(color => color), ) 
  2. Using multicast():

    of('red', 'blue', 'green', 'orange').pipe(   multicast(     () => new ReplaySubject(1),     subject => subject.pipe(       takeWhile((c) => c !== 'green'),       concat(subject.take(1),     )   ), ) 

I've been using this operator as well so I made it to my own set of additional RxJS 5 operators: https://github.com/martinsik/rxjs-extra#takewhileinclusive

This operator has been also discussed in this RxJS 5 issue: https://github.com/ReactiveX/rxjs/issues/2420

Jan 2019: Updated for RxJS 6

like image 168
martin Avatar answered Sep 27 '22 21:09

martin


UPDATE March 2019, rsjx version 6.4.0: takeWhile finally have an optional inclusive parameter that allows to keep the first element that breaks the condition. So now the solution would be simply to pass true as the second argument of takeWhile:

import { takeWhile } from 'rxjs/operators'; import { from } from 'rxjs';  const cutOff = 4.5 const example = from([2, 3, 4, 5, 6]) .pipe(takeWhile(v => v < cutOff, true )) const subscribe = example.subscribe(val =>   console.log('inclusive:', val) ); 

outputs:

inclusive: 2 inclusive: 3 inclusive: 4 inclusive: 5 

Live here:

https://stackblitz.com/edit/typescript-m7zjkr?embed=1&file=index.ts

Notice that 5 is the first element that breaks the condition. Notice that endWith is not really a solution when you have dynamical conditions like v < cutOff and you don't know what will be your last element.

Thanks @martin for pointing out the existence of this pull request.

like image 28
Batato Avatar answered Sep 27 '22 21:09

Batato