1. Introduction
Following the Introduction to RxJava, we’re going to look at the filtering operators.
In particular, we’re going to focus on filtering, skipping, time-filtering, and some more advanced filtering operations.
2. Filtering
When working with Observable, sometimes it’s useful to select only a subset of emitted items. For this purpose, RxJava offers various filtering capabilities.
Let’s start looking at the filter method.
2.1. The filter Operator
Simply put, the filter operator filters an Observable making sure that emitted items match specified condition, which comes in the form of a Predicate.
Let’s see how we can filter only the odd values from those emitted:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable .filter(i -> i % 2 != 0); filteredObservable.subscribe(subscriber); subscriber.assertValues(1, 3, 5, 7, 9);
2.2. The take Operator
When filtering with take, the logic results in the emission of the first n items while ignoring the remaining items.
Let’s see how we can filter the sourceObservable and emit only the first two items:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable.take(3); filteredObservable.subscribe(subscriber); subscriber.assertValues(1, 2, 3);
2.3. The takeWhile Operator
When using takeWhile, the filtered Observable will keep emitting items until it encounters a first element that doesn’t match the Predicate.
Let’s see how we can use the takeWhile – with a filtering Predicate:
Observable<Integer> sourceObservable = Observable.just(1, 2, 3, 4, 3, 2, 1); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable .takeWhile(i -> i < 4); filteredObservable.subscribe(subscriber); subscriber.assertValues(1, 2, 3);
2.4. The takeFirst Operator
Whenever we want to emit only the first item matching a given condition, we can use takeFirst().
Let’s have a quick look at how we can emit the first item that is greater than 5:
Observable<Integer> sourceObservable = Observable .just(1, 2, 3, 4, 5, 7, 6); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable .takeFirst(x -> x > 5); filteredObservable.subscribe(subscriber); subscriber.assertValue(7);
2.5. first and firstOrDefault Operators
A similar behavior can be achieved using the first API:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable.first(); filteredObservable.subscribe(subscriber); subscriber.assertValue(1);
However, in case we want to specify a default value, if no items are emitted, we can use firstOrDefault:
Observable<Integer> sourceObservable = Observable.empty(); Observable<Integer> filteredObservable = sourceObservable.firstOrDefault(-1); filteredObservable.subscribe(subscriber); subscriber.assertValue(-1);
2.6. The takeLast Operator
Next, if we want to emit only the last n items emitted by an Observable, we can use takeLast.
Let’s see how it’s possible to emit only the last three items:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable.takeLast(3); filteredObservable.subscribe(subscriber); subscriber.assertValues(8, 9, 10);
We have to remember that this delays the emission of any item from the source Observable until it completes.
2.7. last and lastOrDefault
If we want to emit only the last element, other then using takeLast(1), we can use last.
This filters the Observable, emitting only the last element, which optionally verifies a filtering Predicate:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable .last(i -> i % 2 != 0); filteredObservable.subscribe(subscriber); subscriber.assertValue(9);
In case the Observable is empty, we can use lastOrDefault, that filters the Observable emitting the default value.
The default value is also emitted if the lastOrDefault operator is used and there aren’t any items that verify the filtering condition:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable.lastOrDefault(-1, i -> i > 10); filteredObservable.subscribe(subscriber); subscriber.assertValue(-1);
2.8. elementAt and elementAtOrDefault Operators
With the elementAt operator, we can pick a single item emitted by the source Observable, specifying its index:
Observable<Integer> sourceObservable = Observable .just(1, 2, 3, 5, 7, 11); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable.elementAt(4); filteredObservable.subscribe(subscriber); subscriber.assertValue(7);
However, elementAt will throw an IndexOutOfBoundException if the specified index exceeds the number of items emitted.
To avoid this situation, it’s possible to use elementAtOrDefault – which will return a default value in case the index is out of range:
Observable<Integer> sourceObservable = Observable .just(1, 2, 3, 5, 7, 11); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable.elementAtOrDefault(7, -1); filteredObservable.subscribe(subscriber); subscriber.assertValue(-1);
2.9. The ofType Operator
Whenever the Observable emits Object items, it’s possible to filter them based on their type.
Let’s see how we can only filter the String type items emitted:
Observable sourceObservable = Observable.just(1, "two", 3, "five", 7, 11); TestSubscriber subscriber = new TestSubscriber(); Observable filteredObservable = sourceObservable.ofType(String.class); filteredObservable.subscribe(subscriber); subscriber.assertValues("two", "five");
3. Skipping
On the other hand, when we want to filter out or skip some of the items emitted by an Observable, RxJava offers a few operators as a counterpart of the filtering ones, that we’ve previously discussed.
Let’s start looking at the skip operator, the counterpart of take.
3.1. The skip Operator
When an Observable emits a sequence of items, it’s possible to filter out or skip some of the firsts emitted items using skip.
For example. let’s see how it’s possible to skip the first four elements:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable.skip(4); filteredObservable.subscribe(subscriber); subscriber.assertValues(5, 6, 7, 8, 9, 10);
3.2. The skipWhile Operator
Whenever we want to filter out all the first values emitted by an Observable that fail a filtering predicate, we can use the skipWhile operator:
Observable<Integer> sourceObservable = Observable .just(1, 2, 3, 4, 5, 4, 3, 2, 1); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable .skipWhile(i -> i < 4); filteredObservable.subscribe(subscriber); subscriber.assertValues(4, 5, 4, 3, 2, 1);
3.3. The skipLast Operator
The skipLast operator allows us to skip the final items emitted by the Observable accepting only those emitted before them.
With this, we can, for example, skip the last five items:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = sourceObservable.skipLast(5); filteredObservable.subscribe(subscriber); subscriber.assertValues(1, 2, 3, 4, 5);
3.4. distinct and distinctUntilChanged Operators
The distinct operator returns an Observable that emits all the items emitted by the sourceObservable that are distinct:
Observable<Integer> sourceObservable = Observable .just(1, 1, 2, 2, 1, 3, 3, 1); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> distinctObservable = sourceObservable.distinct(); distinctObservable.subscribe(subscriber); subscriber.assertValues(1, 2, 3);
However, if we want to obtain an Observable that emits all the items emitted by the sourceObservable that are distinct from their immediate predecessor, we can use the distinctUntilChanged operator:
Observable<Integer> sourceObservable = Observable .just(1, 1, 2, 2, 1, 3, 3, 1); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> distinctObservable = sourceObservable.distinctUntilChanged(); distinctObservable.subscribe(subscriber); subscriber.assertValues(1, 2, 1, 3, 1);
3.5. The ignoreElements Operator
Whenever we want to ignore all the elements emitted by the sourceObservable, we can simply use the ignoreElements:
Observable<Integer> sourceObservable = Observable.range(1, 10); TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> ignoredObservable = sourceObservable.ignoreElements(); ignoredObservable.subscribe(subscriber); subscriber.assertNoValues();
4. Time Filtering Operators
When working with observable sequence, the time axis is unknown but sometimes getting timely data from a sequence could be useful.
With this purpose, RxJava offers a few methods that allow us to work with Observable using also the time axis.
Before moving on to the first one, let’s define a timed Observable that will emit an item every second:
TestScheduler testScheduler = new TestScheduler(); Observable<Integer> timedObservable = Observable .just(1, 2, 3, 4, 5, 6) .zipWith(Observable.interval( 0, 1, TimeUnit.SECONDS, testScheduler), (item, time) -> item);
The TestScheduler is a special scheduler that allows advancing the clock manually at whatever pace we prefer.
4.1. sample and throttleLast Operators
The sample operator filters the timedObservable, returning an Observable that emits the most recent items emitted by this API within period time intervals.
Let’s see how we can sample the timedObservable, filtering only the last emitted item every 2.5 seconds:
TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> sampledObservable = timedObservable .sample(2500L, TimeUnit.MILLISECONDS, testScheduler); sampledObservable.subscribe(subscriber); testScheduler.advanceTimeBy(7, TimeUnit.SECONDS); subscriber.assertValues(3, 5, 6);
This kind of behavior can be achieved also using the throttleLast operator.
4.2. The throttleFirst Operator
The throttleFirst operator differs from throttleLast/sample since it emits the first item emitted by the timedObservable in each sampling period instead of the most recently emitted one.
Let’s see how we can emit the first items, using a sampling period of 4 seconds:
TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = timedObservable .throttleFirst(4100L, TimeUnit.SECONDS, testScheduler); filteredObservable.subscribe(subscriber); testScheduler.advanceTimeBy(7, TimeUnit.SECONDS); subscriber.assertValues(1, 6);
4.3. debounce and throttleWithTimeout Operators
With the debounce operator, it’s possible to emit only an item if a particular timespan has passed without emitting another item.
Therefore, if we select a timespan that is greater than the time interval between the emitted items of the timedObservable, it will only emit the last one. On the other hand, if it’s smaller, it will emit all the items emitted by the timedObservable.
Let’s see what happens in the first scenario:
TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = timedObservable .debounce(2000L, TimeUnit.MILLISECONDS, testScheduler); filteredObservable.subscribe(subscriber); testScheduler.advanceTimeBy(7, TimeUnit.SECONDS); subscriber.assertValue(6);
This kind of behavior can also be achieved using throttleWithTimeout.
4.4. The timeout Operator
The timeout operator mirrors the source Observable, but issue a notification error, aborting the emission of items, if the source Observable fails to emit any items during a specified time interval.
Let’s see what happens if we specify a timeout of 500 milliseconds to our timedObservable:
TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = timedObservable .timeout(500L, TimeUnit.MILLISECONDS, testScheduler); filteredObservable.subscribe(subscriber); testScheduler.advanceTimeBy(7, TimeUnit.SECONDS); subscriber.assertError(TimeoutException.class); subscriber.assertValues(1);
5. Multiple Observable Filtering
When working with Observable, it’s definitely possible to decide if filtering or skipping items based on a second Observable.
Before moving on, let’s define a delayedObservable, that will emit only 1 item after 3 seconds:
Observable<Integer> delayedObservable = Observable.just(1) .delay(3, TimeUnit.SECONDS, testScheduler);
Let’s start with takeUntil operator.
5.1. The takeUntil Operator
The takeUntil operator discards any item emitted by the source Observable (timedObservable) after a second Observable (delayedObservable) emits an item or terminates:
TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = timedObservable .skipUntil(delayedObservable); filteredObservable.subscribe(subscriber); testScheduler.advanceTimeBy(7, TimeUnit.SECONDS); subscriber.assertValues(4, 5, 6);
5.2. The skipUntil Operator
On the other hand, skipUntil discards any item emitted by the source Observable (timedObservable) until a second Observable (delayedObservable) emits an item:
TestSubscriber<Integer> subscriber = new TestSubscriber(); Observable<Integer> filteredObservable = timedObservable .takeUntil(delayedObservable); filteredObservable.subscribe(subscriber); testScheduler.advanceTimeBy(7, TimeUnit.SECONDS); subscriber.assertValues(1, 2, 3);
6. Conclusion
In this extensive tutorial, we explored the different filtering operators available within RxJava, providing a simple example of each one.
As always, all the code examples in this article can be found over on Github.