Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for takeUntil with predicate function #2420

Closed
jbaudanza opened this issue Feb 27, 2017 · 9 comments
Closed

Request for takeUntil with predicate function #2420

jbaudanza opened this issue Feb 27, 2017 · 9 comments

Comments

@jbaudanza
Copy link

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.

@martinsik
Copy link
Contributor

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.

@jbaudanza
Copy link
Author

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.

@avocadowastaken
Copy link

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

@trxcllnt
Copy link
Member

trxcllnt commented May 3, 2017

also possible w/ multicast:

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

@deadbeef84
Copy link

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.

martinsik added a commit to martinsik/rxjs-extra that referenced this issue Jun 20, 2017
@intellix
Copy link

intellix commented Jan 4, 2018

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),
    );
  }),
);

@progral
Copy link

progral commented Mar 9, 2018

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

@MatthiasKunnen
Copy link
Contributor

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

@cartant
Copy link
Collaborator

cartant commented Sep 27, 2018

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

@cartant cartant closed this as completed Sep 27, 2018
@lock lock bot locked as resolved and limited conversation to collaborators Oct 27, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants