Rxjs: Request for takeUntil with predicate function

Created on 27 Feb 2017  路  9Comments  路  Source: ReactiveX/rxjs

I've run into an issue several times where I'm usingtakeWhile but I also want to include the last element from the source observable that triggered the predicate function. For example:

Observable.of('red', 'blue', 'green', 'orange').takeWhile(color => color !== 'green')

gives me:

'red', 'blue'

but I want:

'red', 'blue', 'green'

I haven't found a graceful way to compose this behavior using existing operators. Ideally, I would like to have a derivative of takeWhile that adds an additional call to next() here: takeWhile.ts#L85

RxJava 1.x implemented this by overloading takeUntil to also take a predicate function. You can find the thread discussing this here.

Let me know if there's any interest in this. I'd be happy to put together a PR.

Most helpful comment

I understand that you don't want to add too many operators to avoid bloat and confusion, but I would love to see an operator similar to takeWhileInclusive. I need this behavior relatively often and while the multicast solution is a nice, I would prefer an operator to help readability.

All 9 comments

It's funny that last week I've encountered exactly the same thing and I was wondering if I'm just dumb or there's really no easy way to do this.

I ended up using the following:

Observable.of('red', 'blue', 'green', 'orange')
  .concatMap(color => {
    if (color === 'green') {
      return Observable.of(color, null);
    }
    return Observable.of(color);
  })
  .takeWhile(color => color)
  .subscribe(color => console.log(color));

This produces the result you want but is very confusing and inefficient.

Thanks @martinsik, that's pretty clever. I still think an operator would be nice, especially since there's precedent in RxJava. I'll keep this as a workaround though.

Thanks to http://stackoverflow.com/a/35800173/1709679 I figured out how to make it with work with let:

const takeWhileInclusive = predicate => source =>
  new Observable(observer => {
    const subscription = source.subscribe({
      next: value => {
        observer.next(value);

        if (!predicate(value)) {
          observer.complete();
        }
      },
      error: error => observer.error(error),
      complete: () => observer.complete()
    });

    return () => subscription.unsubscribe();
  });

Observable.of("red", "blue", "green", "orange")
  .let(takeWhileInclusive(color => color !== "green"))
  .subscribe({ next: value => console.log(`takeWhileInclusive: ${value}`) });

Working example: http://jsbin.com/mekupugeza/edit?js,console

also possible w/ multicast:

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

I understand that you don't want to add too many operators to avoid bloat and confusion, but I would love to see an operator similar to takeWhileInclusive. I need this behavior relatively often and while the multicast solution is a nice, I would prefer an operator to help readability.

Great stuff that the multicast works, thanks for that. It's fairly verbose and doesn't come to mind when I'm thinking of how I can compose current operators to achieve it :( It's even more verbose with lettables:

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

I'm using takeWhileInclusive in probably every project and giving a +1 here. Perhaps there's a better way but I'm needing it commonly for countdowns as well:

// Countdown time left until next turn
countdown$ = this.game$.pipe(
  map(game => game.scheduledAt),
  switchMap(time => {
    return timer(0, 16).pipe(
      map(() => {
        const diff = time - Date.now();
        return diff < 0 ? 0 : diff / 1000;
      }),
      takeWhileInclusive(v => v > 0),
    );
  }),
);

I use it also very often. Would love to see it as operator out of the box

For now I've created a library for this operator that can be found here: https://www.npmjs.com/package/rxjs-take-while-inclusive.

Closing this because an inclusive parameter is being added to takeWhile. See #4000 and #4115.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

LittleFox94 picture LittleFox94  路  3Comments

matthewwithanm picture matthewwithanm  路  4Comments

cartant picture cartant  路  3Comments

benlesh picture benlesh  路  3Comments

Zzzen picture Zzzen  路  3Comments