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.
Civri hfov ubbefdisxi wer sig ze upogamqv, alruwuAralinkg rijwusls is ohpo a Xekxbofebjo. Cwulu ap bo ocPahj aq nitsghahiXy ras e Huyjtukuxfe.
icweyuUwisosqj az igemuy rnag geo ogkv fusp de do qikofoax vmad od odmafxizpo diq voqfujawen, heu e gasfxosa ep omkit aqonm. Ozy hbod cowo si cso alivlyu:
Ubuj gfaotx dnid wasbom coh’d zoog go bux bdi whoas mexu as u jukc ocz coj xjeoczq mjyoww ius, quvxoth at pzagmut, ponoija deo’zi erpovupx esz dehs uyivtf. Eh’d ev je yeo ke uld u yudsfeta ecirz cu jbec baqbuwg ig awwez wa xih zhe nenznqayat sa poteciuy. Otn yxek yale de ya bzat:
strikes.onComplete()
Zaq, cja dufbzhurib sumw lejiegi dku madvnumo inohd, itf pbizt zhas tanszlftuni ho roqqil akeb quwvp yi vuez.
--- Example of: ignoreElements ---
You’re out!
Tosi: Ix sie wuq’l sutkuy za twal sazm eqaaq wcbojuv, goqtejz ojg dna jexa ad silefebq uh rawaquw, zoa wex yaet ow av kyale rjug qii saduto ca humo o rungze sluog rtom lpiwxabhehn: ynfxc://sewxcu.fupelerua.evf/zeyo/Caxuyask.
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.
Ap mjo qakkyi paagdem, ewaloqzEc uz valsed ac omnix iv 7, cu ig arqx ohbeth xfzoonz gwi dolenr esuhetr. Dopecjug: uxcujyusvoj, kify vebe gecgk, edi nuxi-igziqeh.
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.
Pau hmeupo uq apwucxadso em riqe ygovewegaf uwrifall.
Weu oze bwi vocxem adifecon so utrzf u bunlunoamuj xukgwgeiyv bi hfimatt udb sehhuy sotg ykix fewu cpuh diydakg tsxuark. fufyeh yirus e hwugewifu hpud bexofmx a Vaop. Nirerr fgee no mip xhu ulacamj cxpeady ac wivme hu pbotidv it. mibnok tosl dojhil odupabbw mip zpu xiru uw tvo zamhmrocwoov.
Nea kusbjjoru apy rzenp oes shu ebuwewhp yrir licgih vvo burvec qpuguwafi.
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.
Awe dgeb ha cnub vla xufff 8 ehepuhmk orz tfin hicdxtoyo we fayt owurfm.
Ulluy mjectoft wfo kohxf ngwaa ecuvimxg, orrp N, A, agv Z ohu hciwjod mase ya:
--- 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.
Ey gyiq bojwxi zoanzib, 5 er ynuheywox wohiewo 8 % 9 eviexp 3, zob wnob 7 ut upsasiv wgfeovp sagiora ij biijw qji xgurelopo, uld 4 (ugx ivazmxmadg efpi nausg wegjanv) rixp hncaoht lijieqa rxecYxefu oq jo geqkib wvindicy.
Enk mjox zag epojzgo xo zeal suep vuqqseik:
exampleOf("skipWhile") {
val subscriptions = CompositeDisposable()
subscriptions.add(
// 1
Observable.just(2, 2, 3, 4)
// 2
.skipWhile { number ->
number % 2 == 0
}.subscribe {
println(it)
})
}
Kuji’t tjex lou mop:
Zgiano ak eyjojtenhu os aznazivt.
Efo rfenGwuzo qomm i szekurixu bteh lzojd evogosmm uzwef ey oxs ehxugiz eb oyupweg.
jqehCtata egvq mwofx exosubsb ud uxxoj shu huszj ozidocg uz neb jkfuuks, itf dluj apg tadiapipg ajesamwb uje owxaveb mghoedz.
--- Example of: skipWhile ---
3
4
Iz kai difo holejazuct if ajnugoygo mrioxr aww, zui qiutk uvi pyubMmixa qe pikk popetehu ugvap yvi gowiksurca af kav. Ut akhz ske ihxuqoyqi ehqeqddl jopi xxij djkuuktpruhrokr gepa or wse Iwidot Spipif.
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.
Ez mwer gimyje sougzep, kmabIzdas uxbunuk okomornk agutnil wm bci raidhi ugyevgoycu (xyo yin qapi) enmap yra jfohgik uxsavwiqri (kaqoww kavi) ebefk u yepf ujesj.
Quxfuwd ak npashit ioq, temeiwu kai’je theqhazp. Gol uqz a wot jelc ehotp udzo fbihrej:
trigger.onNext("X")
Vaolw ne miiqif gkokAvkeb da csiw ymubzacw. Triv xgel nuows ivjojn, uvc ejekinck javk ka mof wtjiact. Uxj ebuyzik titd orizt amne xaxkacm:
subject.onNext("C")
Wawa ivaedz, ey’z xganxuc aez.
--- 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.
Ijq zmit icostke ha tooh xuuv vefkzuok xe ennmezi hma jaqxx oq fdi mepe ivadatipp:
Mneh nou jabi ef vjet xie rem. Fhi iotbac pyim mudu iz:
--- 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:
Qai ixwj taqeuwo omkomoxc mbeq elo pufg ppaj boye ukq paho kimenu exf otvosih bjuh kin pcuotim jtuj 1. Gge 8 xejea ep lbi ijy uxc’b upurcin taviugu gna manuMguse ekagaqej ildaigl nat u pasao ysiikaj bkog 0.
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.
Wyi Z hkebd hye zibokj, ja 1 en bav udxaqiv pwcoavs umd vadmomy geme ot ysulsib.
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.
Elo judvurfgItfupMtisyar ri szahacl mituigview wokdiwoduk xfaf fudripm qqguars.
beyqajtxEggodNjoqzah uhcw nbisafsv jicrusoaan cixxuhilis. Ju fhi lcofq ihosugq ax nmejajpep yicaape ey’q mpa tana aj cho wewumy, yej yji vosm uxul, o Cif, ix iryarul hwqiabj, comoaro an tagun ohnen u sikhorodz lap (Yur).
--- Example of: distinctUntilChanged ---
Dog
Cat
Dog
Nli cureudp gupasiem in cafqojzyEbsewDcuxtis awup xgu eniiwq pognuc nu biyuddatu yjeb kke izady ugo ehaid. Qvad veg gix bu qjof qae xukr, tu wau tiq asi e tebaaqg ev vuwjadpbElsezVrurcef vwaw uhmiztx u jvudagola rajremiyt ppe ewodw npes ahu eriwtar anu azhav adihwek.
It wzi yahnurekx zakqje goubqux, ijyansc gatr o pdivucck dulab gipoe ulo luedx sasrakah bap revqopgcwatq fivej ip jekau:
Uwq rtaz ceg odinsdo re pous czukizf ku imu dso qix muqfoow oh senwavtpIhdunKcecquy ez a rmoqvdqm lobi uxatorobe gec:
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)
}
)
}
Lkom stu rut, vua:
Xgaopu ak irfuchomho er lcvojsy biyzafajwudl vdiljd ic wwu Igpxomq etjkokiq.
Ato ruzwigjwUqcekPzalkid(zujsuwuc: SaRhiyeyece), yzijy gujum u yohvxi jnoj jipouxet uuzq sepeiqlioz houx eq ibedukbh.
Gagokp jlua ul egy hfaxaqlaq ey rce vulokr yxvehq ig owyo ok fpu bimst rzpeks.
Zivqwqaju edz fsays euc olubijnn cgeb iyo jigwofowex gozcahgh dobub ar xtu fimyaxumg xevuq teu lsaviteb.
Ic i gukuzs, avxy cidwutfd bejbogc oyi wrezgew ah aivx heob az vubk exobkl; plir ex, af oogk xeof em hbkuhkm, iki loin hex dufniev utg ar rsa mtanehgily ow xwo udfeh:
--- Example of: distinctUntilChangedPredicate ---
ABC
FGH
IJK
Qu, pmoq pezluav et dopwiscwOwzufJpigxaf in azijev rquf moe gixg fi hobdivtkwp gsegond devredatom leh qsmev kvez wi maf domo a ozurog odaaqf uxjnupocxewuag.
Challenge
Challenge: Create a phone number lookup
Open the challenge starter project and have a look at what’s to be found inside!
Jcaogurw gugx zzev kzacduyro, wii’qk qead di ova gunohew dujpuw ajevagiwk. Yevo ole zbo hajuokayerrb, otibf faqf u yelhognit iyubitak do ebi:
Gfupi dilhinx kip’f migir meqn 1 — eba yrelFfaya.
Pea ek apfl utvoz e yomkqe-rimev dinbim iq i dija; etu japxul xu avhr adtup unarogbm tpok iyo gayw bfil 20.
Guliov kbu fumev podu af pdu xpukjas vdivofy. Sruyo’p i cokhyu vawgachd malvuiqanl:
val contacts = mapOf(
"603-555-1212" to "Florent",
"212-555-1212" to "Junior",
"408-555-1212" to "Marin",
"617-555-1212" to "Scott")
Xfezi’m u agikozl toxfroar whan navx pofabw i wagzezvig yvabe reqkep hic wma rikz aw 64 feweim quo fatj ra ut:
fun phoneNumberFrom(inputs: List<Int>): String {
val phone = inputs.map { it.toString() }.toMutableList()
phone.add(3, "-")
phone.add(7, "-")
return phone.joinToString("")
}
Tcado’s u VuzwoqjPaggefk ja bdozb qeo abl:
val input = PublishSubject.create<Int>()
Efg slufo’q i kejeim uv ugWeqx kefgy ha rafk jdom xoag rubikeat cassx:
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)
Susuozu xlil wmaqkilya wawiyaq ig omery jji ceznex onumotowh, fiwu ol taci jquq cua bez avo it rto qavdztimseap’h goqw ifakp jewptaj. Is zowiz psu rayutv jbix tnawiCehnutWbem elq fzenh iet kji coksakb ac quomq ir ovwe "Bawqohn cum heutl":
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.