Filtrer les observables dans RxJava

Filtrage des observables dans RxJava

1. introduction

Après lesIntroduction to RxJava, nous allons nous intéresser aux opérateurs de filtrage.

En particulier, nous allons nous concentrer sur le filtrage, les sauts, le filtrage temporel et certaines opérations de filtrage plus avancées.

2. Filtration

Lorsque vous travaillez avecObservable, il est parfois utile de ne sélectionner qu’un sous-ensemble d’éléments émis. Pour cela,RxJava offers various filtering capabilities.

Commençons par regarder la méthodefilter.

2.1. L'opérateurfilter

Simply put, the filter operator filters an Observable making sure that emitted items match specified condition, qui se présente sous la forme d'unPredicate.

Voyons comment nous pouvons filtrer uniquement les valeurs impaires de celles émises:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable
  .filter(i -> i % 2 != 0);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(1, 3, 5, 7, 9);

2.2. L'opérateurtake

Lors du filtrage avectake, la logique entraîne l'émission des premiers élémentsn tout en ignorant les éléments restants.

Voyons comment nous pouvons filtrer lessourceObservable et émettre uniquement les deux premiers éléments:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable.take(3);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 3);

2.3. L'opérateurtakeWhile

Lors de l'utilisation detakeWhile,, lesObservable filtrés continueront d'émettre des éléments jusqu'à ce qu'il rencontre un premier élément qui ne correspond pas auxPredicate.

Voyons comment nous pouvons utiliser lestakeWhile - avec un filtragePredicate:

Observable sourceObservable = Observable.just(1, 2, 3, 4, 3, 2, 1);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable
  .takeWhile(i -> i < 4);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 3);

2.4. L'opérateurtakeFirst

Chaque fois que nous voulons émettre uniquement le premier élément correspondant à une condition donnée, nous pouvons utilisertakeFirst().

Voyons rapidement comment nous pouvons émettre le premier élément supérieur à 5:

Observable sourceObservable = Observable
  .just(1, 2, 3, 4, 5, 7, 6);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable
  .takeFirst(x -> x > 5);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(7);

2.5. Opérateursfirst etfirstOrDefault

Un comportement similaire peut être obtenu en utilisant l'APIfirst:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable.first();

filteredObservable.subscribe(subscriber);

subscriber.assertValue(1);

Cependant, si nous voulons spécifier une valeur par défaut, si aucun élément n'est émis, nous pouvons utiliserfirstOrDefault:

Observable sourceObservable = Observable.empty();

Observable filteredObservable = sourceObservable.firstOrDefault(-1);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(-1);

2.6. L'opérateurtakeLast

Ensuite, si nous voulons émettre uniquement les derniers élémentsn émis par unObservable, nous pouvons utilisertakeLast.

Voyons comment il est possible d'émettre uniquement les trois derniers éléments:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable.takeLast(3);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(8, 9, 10);

Nous devons nous rappeler que cela retarde l'émission de tout élément de la sourceObservable jusqu'à ce qu'il se termine.

2.7. last etlastOrDefault

Si nous voulons n'émettre que le dernier élément, sinon en utilisanttakeLast(1), nous pouvons utiliserlast.

Ceci filtre lesObservable, n'émettant que le dernier élément, qui vérifie éventuellement un filtragePredicate:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable
  .last(i -> i % 2 != 0);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(9);

Dans le cas où leObservable est vide, nous pouvons utiliserlastOrDefault, qui filtre lesObservable émettant la valeur par défaut.

La valeur par défaut est également émise si l'opérateurlastOrDefault est utilisé et qu'aucun élément ne vérifie la condition de filtrage:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable =
  sourceObservable.lastOrDefault(-1, i -> i > 10);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(-1);

2.8. OpérateurselementAt etelementAtOrDefault

Avec l'opérateurelementAt, on peut choisir un seul élément émis par la sourceObservable, en spécifiant son index:

Observable sourceObservable = Observable
  .just(1, 2, 3, 5, 7, 11);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable.elementAt(4);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(7);

Cependant,elementAt lèvera unIndexOutOfBoundException si l'index spécifié dépasse le nombre d'éléments émis.

Pour éviter cette situation, il est possible d’utiliserelementAtOrDefault – qui renverra une valeur par défaut au cas où l’index serait hors limites:

Observable sourceObservable = Observable
  .just(1, 2, 3, 5, 7, 11);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable
 = sourceObservable.elementAtOrDefault(7, -1);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(-1);

2.9. L'opérateurofType

Chaque fois que leObservable émet des élémentsObject, il est possible de les filtrer en fonction de leur type.

Voyons comment nous ne pouvons filtrer que les éléments de typeString émis:

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

Par contre, lorsque nous voulons filtrer ou ignorer certains des éléments émis par unObservable,RxJava offers a few operators as a counterpart of the filtering ones, dont nous avons déjà parlé.

Commençons par regarder l'opérateurskip, la contrepartie detake.

3.1. L'opérateurskip

Lorsqu'unObservable émet une séquence d'éléments, il est possible de filtrer ou d'ignorer certains des premiers éléments émis en utilisantskip.

Par exemple. voyons comment il est possible d'ignorer les quatre premiers éléments:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable.skip(4);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(5, 6, 7, 8, 9, 10);

3.2. L'opérateurskipWhile

Chaque fois que nous voulons filtrer toutes les premières valeurs émises par unObservable qui échouent à un prédicat de filtrage, nous pouvons utiliser l'opérateurskipWhile:

Observable sourceObservable = Observable
  .just(1, 2, 3, 4, 5, 4, 3, 2, 1);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable
  .skipWhile(i -> i < 4);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(4, 5, 4, 3, 2, 1);

3.3. TheskipLast Opérateur

L'opérateurskipLast permet de sauter les éléments finaux émis par lesObservable en acceptant uniquement ceux émis avant eux.

Avec cela, nous pouvons, par exemple, ignorer les cinq derniers éléments:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable.skipLast(5);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 3, 4, 5);

3.4. Opérateursdistinct etdistinctUntilChanged

L'opérateurdistinct renvoie unObservable qui émet tous les éléments émis par lessourceObservable distincts:

Observable sourceObservable = Observable
  .just(1, 1, 2, 2, 1, 3, 3, 1);
TestSubscriber subscriber = new TestSubscriber();

Observable distinctObservable = sourceObservable.distinct();

distinctObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 3);

Cependant, si nous voulons obtenir unObservable qui émet tous les éléments émis par lessourceObservable distincts de leur prédécesseur immédiat, nous pouvons utiliser l'opérateurdistinctUntilChanged:

Observable sourceObservable = Observable
  .just(1, 1, 2, 2, 1, 3, 3, 1);
TestSubscriber subscriber = new TestSubscriber();

Observable distinctObservable = sourceObservable.distinctUntilChanged();

distinctObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 1, 3, 1);

3.5. L'opérateurignoreElements

Chaque fois que nous voulons ignorer tous les éléments émis par lessourceObservable, nous pouvons simplement utiliser lesignoreElements:

Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();

Observable ignoredObservable = sourceObservable.ignoreElements();

ignoredObservable.subscribe(subscriber);

subscriber.assertNoValues();

4. Opérateurs de filtrage temporel

Lorsque vous travaillez avec une séquence observable, l’axe temps est inconnu, mais il peut parfois être utile d’obtenir des données ponctuelles à partir d’une séquence.

Dans ce but,RxJava offers a few methods that allow us to work with Observable using also the time axis.

Avant de passer au premier, définissons unObservable chronométré qui émettra un élément toutes les secondes:

TestScheduler testScheduler = new TestScheduler();

Observable 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 au rythme que nous préférons.

4.1. Opérateurssample etthrottleLast

L'opérateursample filtre lestimedObservable, renvoyant unObservable qui émet les éléments les plus récents émis par cette API dans des intervalles de temps.

Voyons comment nous pouvons échantillonner lestimedObservable, en filtrant uniquement le dernier élément émis toutes les 2,5 secondes:

TestSubscriber subscriber = new TestSubscriber();

Observable sampledObservable = timedObservable
  .sample(2500L, TimeUnit.MILLISECONDS, testScheduler);

sampledObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValues(3, 5, 6);

Ce type de comportement peut également être obtenu en utilisant l'opérateurthrottleLast.

4.2. L'opérateurthrottleFirst

L'opérateurthrottleFirst diffère dethrottleLast/sample car il émet le premier élément émis par lestimedObservable dans chaque période d'échantillonnage au lieu du dernier émis.

Voyons comment nous pouvons émettre les premiers éléments, en utilisant une période d'échantillonnage de 4 secondes:

TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = timedObservable
  .throttleFirst(4100L, TimeUnit.SECONDS, testScheduler);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValues(1, 6);

4.3. Opérateursdebounce etthrottleWithTimeout

Avec l’opérateurdebounce, il est possible d’émettre un élément uniquement si une période donnée s’est écoulée sans émettre d’autre élément.

Par conséquent, si nous sélectionnons une durée supérieure à l'intervalle de temps entre les éléments émis destimedObservable, il n'émettra que le dernier. Par contre, s'il est plus petit, il émettra tous les items émis par lestimedObservable.

Voyons ce qui se passe dans le premier scénario:

TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = timedObservable
  .debounce(2000L, TimeUnit.MILLISECONDS, testScheduler);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValue(6);

Ce type de comportement peut également être obtenu en utilisantthrottleWithTimeout.

4.4. L'opérateurtimeout

L'opérateurtimeout reflète la sourceObservable, mais émet une erreur de notification, annulant l'émission des éléments, si la sourceObservable n'émet aucun élément pendant un intervalle de temps spécifié.

Voyons ce qui se passe si nous spécifions un délai d’expiration de 500 millisecondes pour nostimedObservable:

TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = timedObservable
  .timeout(500L, TimeUnit.MILLISECONDS, testScheduler);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertError(TimeoutException.class); subscriber.assertValues(1);

5. Filtrage observable multiple

Lorsque vous travaillez avecObservable, il est certainement possible de décider si filtrer ou ignorer des éléments en fonction d'un secondObservable.

Avant de continuer, définissons undelayedObservable, qui n'émettra qu'un seul élément après 3 secondes:

Observable delayedObservable = Observable.just(1)
  .delay(3, TimeUnit.SECONDS, testScheduler);

Commençons par l'opérateurtakeUntil.

5.1. L'opérateurtakeUntil

L'opérateurtakeUntil rejette tout élément émis par la sourceObservable (timedObservable) après qu'une secondeObservable (delayedObservable) émet un élément ou se termine:

TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = timedObservable
  .skipUntil(delayedObservable);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValues(4, 5, 6);

5.2. L'opérateurskipUntil

D'autre part,skipUntil rejette tout élément émis par la sourceObservable (timedObservable) jusqu'à ce qu'un secondObservable (delayedObservable) émette un élément:

TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = timedObservable
  .takeUntil(delayedObservable);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValues(1, 2, 3);

6. Conclusion

Dans ce didacticiel complet, nous avons exploré les différents opérateurs de filtrage disponibles dans RxJava, en fournissant un exemple simple de chacun.

Comme toujours, tous les exemples de code de cet article peuvent être trouvésover on GitHub.