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.
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.
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.