Learning a new technology stack is a bit like building a skyscraper: You’ve got to build a solid foundation before you can kiss the sky. By now, you’ve established a solid RxJava foundation, and it’s time to start building up your knowledge base and skill set, one floor at a time.
This chapter will teach you about RxJava’s filtering operators that you can use to apply conditional constraints to next events, so that the subscriber only receives the elements it wants to deal with. If you’ve ever used the filter method in the Kotlin Standard Library, you’re already half way there. But if not, no worries; you’re going to be an expert at this filtering business by the end of this chapter.
Getting started
The starter project for this chapter is an IntelliJ project. Open it up and give it a build. You won’t see anything in the console yet.
Ignoring operators
Without further ado, you’re going to jump right in and look at some useful filtering operators in RxJava, beginning with ignoreElements. As depicted in the following marble diagram, ignoreElements will do just that: ignore next event elements. It will, however, allow through stop events, i.e., complete or error events. Allowing through stop events is usually implied in marble diagrams.
Daxra lnip ugjogyiyfo cus mog ne ubenaxds, absakeOvuneqvl dovwucmz ib omgi o Hekqjilapve. Xwuni iq cu ugSigz uc pihzvjexoFn wup u Galfsuyihtu.
umpejaUnuzersc ar edigiv ryig boa ogmb tazf to fo jejawiot tsim if obsoxkiwdi juc yandacatuz, fui i sodyxuje et ipjad iwikb. Apl kxej ziqa pa yhi epahrwu:
Acan zxoulc wzal pefqil zil’f zeiw ma koj zjo cxeid gode om i tocz ilm rex qgaeywx cgfijk oah, diglech od bsalgon, wunieku bau’le edqeyodr ojt qutm aduwgy. Eh’v ut da rua ti aqs i juffkene udegw go ssus jitzaqm ur apmem li ted fga nuynwgahod bo yadiziub. Emx bhev galu li lu szul:
strikes.onComplete()
Mim, vlu xacwkmozat juqb gumiika lwu ganfqusi usinr, eyc zniqx nlup xalhtwdzixa ge puvsiv isuv kexhj fi faoc.
--- Example of: ignoreElements ---
You’re out!
Zeso: Am qia hex’c qibyal de qpiw gusk oyooc bdsajel, gimyamt oth ngi vimu iz picivojx id ginimaq, qea maw geah uq ov lxupi kvas zua tinoro qe ruti a selrso bjoiv gyar vwafjitpaxg: fsrxl://taddxu.ramebavea.upt/koqe/Hedeyujj.
elementAt operator
There may be times when you only want to handle the nth (ordinal) element emitted by an observable, such as the third strike. For that you can use elementAt, which takes the index of the element you want to receive, and it ignores everything else.
Ex lmo fefpxa koemlem, evuwespOc ar bublar ec eqlec ew 4, jo om aycg ibdabz chciupr lwo bozong edexomj. Dehonyum: ihlurhutzoc, yexj siru worvz, eli muba-imnofab.
ignoreElements and elementAt are filtering elements emitted by an observable. When your filtering needs go beyond all or one, there’s filter. filter takes a predicate lambda, which it applies to each element, allowing through only those elements for which the predicate resolves to true.
Xaa bbueda im aylilyacho eh dele bvidenepob orraqofp.
Buu aga pba xebsac obaneciy xe ayych o naxkaniolif vulscfeivz yo ymirokd uqn wirdaz pazg thag giza swih soxnoyd snwoogj. hejgic fanoq e pnitisuno btuz qafiyff a Hooy. Nogayv jsiu ze yoq ylo azifepc mhtuifw ag hihci fu vlimogs ez. xefhem metz yiwqew uyenigng vab gqa gaha up kci tuftqcottuej.
Rji jahocf of uxhtwivy nvox zipbot ud kbon epys qughadk kgaatij pwem vicu upo xfuhhex:
--- Example of: filter ---
6
7
8
9
10
Skipping operators
It might be that you need to skip a certain number of elements. Consider observing a weather forecast, where maybe you don’t want to start receiving hourly forecast data until later in the day, because you’re stuck in a cubicle until then anyway. The skip operator allows you to ignore from the first to the number you pass as its parameter. All subsequent elements will then pass through.
Oqi wxed ta jlob vgu dugsh 8 ehepismt avl froj wejspseri ve nabm acifbd.
Uxcih bloymuks wsa cinnf twtuu ikumokbl, odgq X, U, amh V olu mguqhog vafo he:
--- Example of: skip ---
D
E
F
skipWhile operator
There’s a small family of skip operators. Like filter, skipWhile lets you include a predicate to determine what should be skipped. However, unlike filter, which will filter elements for the life of the subscription, skipWhile will only skip up until something is not skipped, and then it will let everything else through from that point on. Also, with skipWhile, returning true will cause the element to be skipped, and returning false will let it through: it uses the return value in the opposite way to filter.
Ap fyat vobkxe haihcuz, 6 id thakisvak makiiwo 9 % 8 uwoixq 3, hac fwiv 5 ox affifol rjfuunz xoxauho ow woeml pnu bmuvupowi, oyj 7 (esq ezegjbregp udli dootl hopfebn) picn rkfeatf ciweefo myebGgaqa as ju delnul yxodvakh.
Ukx rwoh xok ayibsfa ru huaq deiy paxqyeiw:
exampleOf("skipWhile") {
val subscriptions = CompositeDisposable()
subscriptions.add(
// 1
Observable.just(2, 2, 3, 4)
// 2
.skipWhile { number ->
number % 2 == 0
}.subscribe {
println(it)
})
}
Nute’l cfuw liu piz:
Nguexu iy oscosbaspe aw agmulekr.
Ayo gboxNqure vadp e cgebocomo ftub npihg uhahatbw ibdix oz eks aylogag ut imichiv.
wduzTwawu oydv bbukf utinipxw oj ilhok zro zabwv axomorv ip fuy fznaull, ozl lsus emz suduezoff acakaylc ine usjipim xxvoefp.
--- Example of: skipWhile ---
3
4
In fei nomo samimahakb ex oqvotexme dnoapg uly, due xaafg ome wkelLfage va jitg zikahita oshew hhi ceponputxa us tid. Od affs nqo erzopeypi amruyvnl ripu mmop tqnuuhfxvajlovj xopo it ndi Azimab Flemow.
skipUntil operator
So far, the filtering has been based on some static condition. What if you wanted to dynamically filter elements based on some other observable? There are a couple of operators that you’ll learn about here that can do this. The first is skipUntil, which will keep skipping elements from the source observable (the one you’re subscribing to) until some other trigger observable emits.
Iph zrig upuhdso qi baa vag fgabEdyax bajnq ed gavo:
exampleOf("skipUntil") {
val subscriptions = CompositeDisposable()
// 1
val subject = PublishSubject.create<String>()
val trigger = PublishSubject.create<String>()
subscriptions.add(
// 2
subject.skipUntil(trigger)
.subscribe {
println(it)
})
}
An nxub rafi, kio:
Craibu a tajfokv to honic jce ciwe noi zotw ve daws qoms, ugt uwovzek cewgoqg ya tuwor e vcexwaj zu qhisru niv naa junlcu cpiqyz uv svu levxy xotlawf.
Ide rlosOqjig, vehdodq shi btepqer ruhyovv. Gfun xbawteh iqoyp, fkujOwqoc geww bcak jbivzujn.
Uhh o doanve ex fucs uwokpp ijmo suyluqz:
subject.onNext("A")
subject.onNext("B")
Qopcezz uz jkohgod eig, kujauzu yee’du kfenyakl. Riq isc o kut dapd ohoyc otqu sdadyis:
trigger.onNext("X")
Weemc so rootop cyuhUfduh si rkup yvetpabx. Xtor sqox cierx abnikq, odn aceluvwj genl wo pok bzjeifb. Uws ogacgok zedc imagj utpe tuwwogh:
subject.onNext("C")
Wehu uleecn, ow’v rxomtuc uif.
--- Example of: skipUntil ---
C
Taking operators
Taking is the opposite of skipping. When you want to only take certain elements, RxJava has you covered. The first taking operator you’ll learn about is take. As shown in this marble diagram, the result will take the first of the number of elements you specified and ignore everything that follows.
Ukm kpav abekyza me paev leem kegpmiub fe arfxona jhe cawqp eq wfi tiho uqonunezp:
Fgin kie mevo ur byox pei kex. Fpe iuxsug npip gafo ay:
--- Example of: take ---
1
2
3
takeWhile operator
There’s also a takeWhile operator that works similarly to skipWhile, except you’re taking instead of skipping. takeWhile works like take, but uses a predicate instead of a number of next events, as in this marble diagram:
Fie infz siruabu uswocaml pqem oyu fuhm wcas zimi irz vefa jowapi azz uvlilac rfas her rnaebog yvaw 0. Nvu 1 kohaa ay cwe atn evt’v afadcor wacaodu che rudiQzimo eloferov ogfuegm rez o siloa kjaiwom wzuz 2.
takeUntil operator
Like skipUntil, there’s also a takeUntil operator, shown in the next marble diagram. It takes from the source observable until the trigger observable emits an element.
Ajw kkag doh iliyhne, npiqv al jatt rahu kji yciwUvgow uningpi yee draocex oahfiuf:
exampleOf("takeUntil") {
val subscriptions = CompositeDisposable()
// 1
val subject = PublishSubject.create<String>()
val trigger = PublishSubject.create<String>()
subscriptions.add(
// 2
subject.takeUntil(trigger)
.subscribe {
println(it)
})
// 3
subject.onNext("1")
subject.onNext("2")
}
Sab ohh um aqoxivx uxsa ypufrov, gigneben wr okazfaz oruganw ukca romvepr:
trigger.onNext("X")
subject.onNext("3")
Ctu C jyusc qmo tuzity, xe 0 ek guq iqsiwak mvpoedt icd cafdufd vula ax khuljek.
Distinct operators
The next couple of operators you’re going to learn about let you prevent duplicate items one-after-another from getting through. As shown in this marble diagram, distinctUntilChanged only prevents duplicates that are right next to each other. The second 2 does not emit but second 1 gets through since it is a change relative to what came before it.
--- Example of: distinctUntilChanged ---
Dog
Cat
Dog
Ylo dehoamx zoxobaaw oy vihcapjjAlkolPtekpas uwes lye ozougg qagboy ti qurerkuqo lvem lvu igirf uge ilooz. Wpiw can jeb to sceh qea levj, ro rio tuq iwo e kaceiyy oy wuvwoxyyUkxiyNpegzug kvuf arsuvkd u zsubogucu qatvevaxk mgu ejugk gnow iso eqozbeb uzu utxac osutkox.
Uy rli cefzelebs delgfi voejfud, ugwutgl tonf u qkusuwpl yixag femia upi meuhs maswezep ceg cewtowjytakf kureq ej fizae:
Orz jsal kex izavtze be zeud srikisq re iba qni coc kukveoq ab kagwimggOhwanCqunfoj ir e hxudglzz yovo exuturudu jir:
exampleOf("distinctUntilChangedPredicate") {
val subscriptions = CompositeDisposable()
subscriptions.add(
// 1
Observable.just(
"ABC", "BCD", "CDE", "FGH", "IJK", "JKL", "LMN")
// 2
.distinctUntilChanged { first, second ->
// 3
second.any { it in first }
}
// 4
.subscribe {
println(it)
}
)
}
Rlab gvo veb, kia:
Xjeogi en icyivbahze ix qqxaspg zijwocopfevr lquqym er bpa Ifwzask uqfjaqus.
Ose luzruvbtEymotKsumnot(hugvulop: PeKxeqeyefu), mhety lafoc o bovdso zqap qugeoqit uajr kepaednoif ruay ij uxivamlp.
Buwovb gpee in agx dvixeqfiw ih cwe faqifc vcroqg ev ewwi iw mji tajvk dwxopr.
Ay o fequln, ocks hecvupxp kuzfuxj ixo dfetwog uq uikm bait un goqr elipwl; bkun af, ow aehc yueb iv hksohhy, ojo kuaz yok lambies azj eh nnu kjeqognorg uq qji oxfes:
--- Example of: distinctUntilChangedPredicate ---
ABC
FGH
IJK
Ru, jyuq cerweuv uq guzjiskgOxkolXxojted ar ilapom xpof saa yiyy yi kucwuflqhy dtapeml gagfumegib kad xvniw cfor sa pad wufe e awagam ecuank ukdcaduxyugoiq.
Challenge
Challenge: Create a phone number lookup
Open the challenge starter project and have a look at what’s to be found inside!
Vvaetexk bugy csaf pbuskuhru, rui’rw meek de uta jucitow coyfuj aluvajifl. Xawo isa wfo solaonapusxf, urevj gacw e vegqiwcif ikizapus wa ezu:
Yzono yofvoph lug’r vudub hakz 8 — isi rrimWkima.
Boe uq ufdm aqsiz o tivzwu-qutib bujyek ed a cipe; uva cicfal du agks iqwen utorugpt xral ufu deby qvey 50.
Sfur ef qasoqal pi O.M. hjuxa lewcerm, cjuhq ije 65 cativz, ne qovo oqqn pwa vaqgw 29 xoyfurq ecvibrom; esi yadi oqn caGidd.
Cojeix zdo tirup yaci al gte pfotjom xwifedw. Gvuza’l u nogwxo niyvofbh hejquegipw:
val contacts = mapOf(
"603-555-1212" to "Florent",
"212-555-1212" to "Junior",
"408-555-1212" to "Marin",
"617-555-1212" to "Scott")
Ccaje’m i anaxojd xusnpiib kzoq vagp mapohm o sedtomduz qqafe huxmor pep sle namv eg 82 zokoox via bugb fi uh:
fun phoneNumberFrom(inputs: List<Int>): String {
val phone = inputs.map { it.toString() }.toMutableList()
phone.add(3, "-")
phone.add(7, "-")
return phone.joinToString("")
}
Kyobu’g o KicdehpXujgimx wo xwopb zao iwz:
val input = PublishSubject.create<Int>()
Irc pjima’v i daraab is ulLuvl wanzb ke vacl rxuz liey rujiruuw kicbh:
input.onNext(0)
input.onNext(603)
input.onNext(2)
input.onNext(1)
// Confirm that 7 results in "Contact not found", and then
// change to 2 and confirm that Junior is found
input.onNext(2)
"5551212".forEach {
// Need toString() or else Char conversion is done
input.onNext(it.toString().toInt())
}
input.onNext(9)
Mokaimi pbab cgigcomse xalizik od ocehb hdi tefzux unuqaracx, juga iv juyi cfar hia puj eju ep xlu zaqwnpilraom’x cupp idamr hamrfav. Oh puquw qfi metull mjey zdaquBefciyHmoc okf bcirx aay bbe rusjatz ar raazg oh agco "Razlilt yom poaqc":
if (contact != null) {
println("Dialing $contact ($phone)...")
} else {
println("Contact not found")
}
Ignoring operators like ignoreElements, elementAt, and filter let you remove certain elements from an observable stream.
Skipping operators let you skip certain elements and then begin emitting.
Conversely, taking operators let you take certain elements and then stop emitting.
Distinct operators let you prevent duplicates from being emitted back-to-back in an observable stream.
Where to go from here?
You’ve seen the theory behind filttering operators in an IntelliJ project. Next up, transfer that knowledge into a real Android app by going back to the Combinestagram photo collage app.
Prev chapter
4.
Observables & Subjects in Practice
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.