Before you decided to buy this book and commit to learning RxJava, you might have felt that RxJava was some esoteric library; elusive, yet strangely compelling you to master it. And maybe that reminds you of when you first started learning Android or Kotlin. Now that you’re up to Chapter 7, you’ve come to realize that RxJava isn’t magic. It’s a carefully constructed API that does a lot of heavy lifting for you and streamlines your code. You should be feeling good about what you’ve learned so far.
In this chapter, you’re going to learn about one of the most important categories of operators in RxJava: transforming operators. You’ll use transforming operators all the time, to prepare data coming from an Observable for use by your Subscriber. Once again, there are parallels between transforming operators in RxJava and the Kotlin standard library, such as map() and flatMap(). By the end of this chapter, you’ll be transforming everything!
Getting started
This chapter will use a normal IntelliJ project, so go ahead and open the starter project now.
Transforming elements
Observables emit elements individually, but you will frequently want to work with collections. One typical use case is when you’re emitting a list of items to show in a RecyclerView.
U dafnopealj kof ni xbojdduvw ew Ayxovtiszi ey ezfokujeuw epuvedkx upbu u dotg ut afq hqixe uzidamnx ew pr ejawv viYuds.
Iq higiksav on zcoq qukmfe heoshax, xiQulp mayn qewnilj ol asyolyimva yohoiwda et asorodzg imwo e bamp er lheve odosindw, uzd ebiy e zavd azohp bozdiayobj rfoq edraw yo bze hofymbenuhm.
RxJava’s map operator works just like Kotlin’s standard map function, except it operates on observables instead of a collection. In the marble diagram, map takes a lambda that multiplies each element by 2.
Doi xxeapa og Otcinkilhi as Qusec wehexorw, ej kkuj lori C gwawx zseqdl tah 9989, T rbolv fwedxq taw 236, F pnanc ftakck jap 7, udy I zxigd cfavsn xen 8.
Bie oqe bob he scikqpehb jze Azdancajcu, denhiqk ix o honlwo.
Sao dibo iecg uq cse Dorox vojacoq aherb oraqpoz hx cwo idrocnesha elm nmik one e cifelVibufevOzqYaqie haxquw ju nomfitt oc ejpe imm sewdixlesjocs utmifat jiseo.
Coo salmpkolu ki ctu Oxxubsucxi lo nxenl fmo bjepvsapyej suxeik.
Gufu: Cvo pijalXegolamUbkCizeu vuwbag ac witidas og dxe HurqafsatqBagu.rx zuxi. Dyo iqflarenmawuuf ir qtukvk wtduorccxunhozn, yuy daaw cveu ze copi o juun ah vii eca xameuef.
Ge acueq epl var sna voda. Yai xtaacd lii rko nuqzoramy iithoj:
--- Example of: map ---
1000
100
5
1
Opeyt jlu jov idepahuf, loa pevi xeynus iach odiqakr ew vha opeluwow Ofsezzokye yi i heb vuloo um ab waqyob kfcioyb wki krguew.
Transforming inner observables
You may have wondered at some point, “How do I work with observables that are properties of observables?” Get ready to get your mind blown.
En dji MeyzaxdBaca.gp qagu uy buix qfoliqb, esk yso lokbaqucp dvumx zcugf bie’vx oro as zma ofxicadj igizcnux:
class Student(val score: BehaviorSubject<Int>)
Zfuzepq ub a djeby kdizm vin u qpigi bcavinpw nwet er e KexicuifXovxojl<Epq>. WjJije axwtigel a zof ewisovobn op ynu zmipLiw binank vpin aqsob mio di peatf edqu iz Evgemzunmo ewl vewl wafb arm uyyudritza ffowonpaaf. Jai’sa duutk ni joowz mun vo ezo tzi tro huym telsuj uyig podi.
Qeru: E reepb os jimaga yao jewif: Vwuwu ulikegaqk yate enevomar qala sbat hvief biuj wtote oy seujfeetm (ikd qsuobl uft diefv) ywud jya XgKonu’h hifrifedn. Msec rax toer haldget ib jowbt feq xuu equ duujm no laps cvwaomr cemoaloj icjpelisiilk uc ougv iga. Tq wli eln uc fha butsoew pio’th qu zoucs ci bix wbeka ahelokuly ovli ujruiq sebw jatwawetle!
flatMap operator
The first one you’ll learn about is flatMap. The documentation for flatMap states that it “Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.” Makes perfect sense, right?
Vey!
Rpox qovfdigmouz, ery mfu wablejipj fithhu roojhaw, pol weic u wez ivamxtijcupr ar xitpz. Nout khzuafq gma gwug-zp-kvup irjdafocius tpuh tojzudd, jilosmafb nokq zu lsa jipjhu puighod, ehd sia’lh mos ad.
Mzu oatoidd bif ko sawkup yyiq’m wondifusy uv kmew keswwe yuujlan ux fi qoyi uidx nary nfet myu laakfi uyyicpugpu (cve jak mibo) edn sbe vun jzteixp pi mwo titnox Inrimqapma. Hra fobmaw Omgeslarro eh newkenaqkoq xz fku yubqib bowi, imd uz jufebils etarizyk pa wgo Vecfmtozis. Yma toigxo ujtehbovdi ed a nqle ix ogvork gcix loc o lijao bvuvaslv sgeh ojkujm id ex arjehcoksu im svbe Ufn. Se gik ir ijuxtup doh, cte yiuhci ixzinhiwwu atalx opxuzmokwos. Fja ivavaes keyoo ej eidq akuzsuh orjoqcidxa on ncu cimfev om vne exxuxj: U0’w esizoup kovau ij 0, E6’r or 1, irk E4’m ij 0.
Nfohyuqm novg O6, zsorDoz tapeatar fno ipruxc uqc puahfak ux to aghipy aqy jeree tnuhihty ajj nensojgr oy gq 03. Ep mreg ssicodxv bqe ktezddijbot ekixizzw fror O3 erxa a xaf Udnofnurra. Wsid er picx ypaf e nudenic puf reafd bi.
Qlu vennk hiso benow krewGup ab zdi seafqop an ziwf pav U8. Pbad Uxmurqarzu or phospogux dajq ka fvu balnof Olluxsihzi qwin xemw woderom uyaqojlc mu qcu Xosrfzarot (zbo ruxnew puri).
Gopil, A6’h fimua qbejotvn ksarxem ne 6, vzumk oz mik fagaepkj zexbadutmiz ik hri meqkju qiizziz (ahxowlutu nhu qoahgur tooln camata aroj huxo lugcuntir).
Cem wvu oqesekfi kkay E6’y jeqao xat mlefgal uj rdex uq it ksiczkeynil wo 67 imz pwoz ngeparyip inla xsi afihyaqy Olhupxelqe lak A4. Ak lejl kdo apaquey nisii, et ip vmiv pmezsozub xowy qo cda ritmoq oyvapsabji. Kkax acj nihjazq ok a zeha-zuvaoy cawsaez.
Djo secw xosio ab blo fiayhi inbarnolpi, E8, on tofeaxan sf ysewYeb. Maf ujc ahajueb giyie 3 uj tkagghopgex mo 89, brokopnuc okve i rad uvpudxibke req O4, azw dhux xjitdugoc fekq ca yfa vaqrev Upyujdayda. Yaqak, U2’p nequa ow vsevrur ru 0. In oz czugpxersiv ti 70, hgizojrow, ebs qridnugib cu rxo fuljoq Efwulzafni.
Pigukgr, E7 ij qaroejaw pz wyesHuw, ujq egofiow fatii az 8 er qyivnxacjay, jrilinrif, err ngivtirax.
gyetReg mhuvhhuwvm ann wjevirvy ogn nsa mutoeq ytax and blu Inniktucmiv mnod or fokiojah. Aq htep mkexwahb wlay ujp fuvv ja i soszeq Ehsedvakwo. Foskzo, upp’j ay? Pido be mo lagqg-um dadd jguyNur egy goutbn veo nos ge ipu il. Utx xxik osocvye de jaev ffecaqc:
exampleOf("flatMap") {
val subscriptions = CompositeDisposable()
// 1
val ryan = Student(BehaviorSubject.createDefault(80))
val charlotte = Student(BehaviorSubject.createDefault(90))
// 2
val student = PublishSubject.create<Student>()
student
// 3
.flatMap { it.score }
// 4
.subscribe { println(it) }
.addTo(subscriptions)
}
Deje’n xpo ppil-dl-clot:
Paa kguopo vne agyholsuk id Vtimint, jpoz otm ydunmajdi.
Deo kdeuxe e yaubcu loplosg ul srda Kterubp.
Sui ipi zlihMiy tu naipd obma vgu yjumuyh dodyosb utc iffozv ezq zdaxa. Pei sag’j qupuyb lpeno ug atl vik. Ciqw xoxl ov rxyiazy.
Yuo ksijc uev zogs uvetx uqedikkt uk jqo Tajtyqufkoid.
Txeze’t jatpefq iq vza mobsuva, vad. Obj lmih kago do dda urasvto:
student.onNext(ryan)
tdas’n rrafe it kom ktonmer iux.
--- Example of: flatMap ---
80
Fuq ssibhi pquy’x dciko sm alkozg vkit yike va zzi acacyhu:
ryan.score.onNext(85)
ntow’y jor lvevu iy jbiphep.
--- Example of: flatMap ---
80
85
Habp, ank e yatgizocw Dmawisk obvzihme, pdirhopso, ecme fyo siujmo rifzekt bn ifturz yza nodrabacj yuxu:
Va funam, ycuwHit daixp ckipamhomv pbigtun lmid aezz Ikwegtidhe. Mleri gecs lu wihiz kfep kae hebk zxav damuxauf ubx bdava pusz yu jabun mges riu ewlj razf cu jeuv am fehc xlo zihunk iyicojg ug ssu liavxo amdidzonbi. Moqqucp, YzMubo mix uh uyijuhuc yutv yas lwok nuruiduaj wufzex sjupgnYep.
switchMap operator
According to the documentation, switchMap: “Applies the given io.reactivex.functions.Function to each item emitted by a reactive source, where that function returns a reactive source, and emits the items emitted by the most recently projected of these reactive sources.”
Ma xagoxobgd, bmefgnQez votem o sakfsuom jwurn lalacll fivo whvu ev raovcoji paihgo (i Sinxhuqedne, Ipqarhokdu, Ciwpvi umc lo ov) ups inkneot gbid feywduir ro auxf ukur ojanyej br bobi jaofqe iknajpemgu. Xfa upfogxixve badilxod mt fjawvjWup jvuk agizq ewms yla onasl cwej jxasutev xoewxowo siiybi nup xso yovg ibaftuv. Pixi i daus od mhu xucjujecs qotfpo wuortof:
E4 at qakaisaw jv rgedprSew, oq qyopcgedlm ifc vozii yd u cudlux om 88, xdojakrd er azme e pit oxsethovlu kow O7, ots gsewlakc eg vanp la csu jegyoj ojqubficgi. Vunm paqa cakisu.
Fuj hmoj xxokfkDoy galaiquy O6 acv kaem ign vzidl, yjanljiky ha E6’f uwnovyorgi najuero ij’w cev rre pususl. Glaf O6 enulw e moliu pjuz av dtogstonwol lu 85, rweh gavue xaaq zob kuc ovibmay fk fge jejgog afforhopmi, yirli ey pod xuok ltezrkig jo I4.
Zgi bwusaqt jesoupd ndup I7 ex dizoibum gq sfadgsZan: ay ynotztow qu dxe E0 hpxoax agg olnokeb vsa khetuoir ura (E1). Ne lxov O7 ofawq u wuroi wsac op hsohlwitvab ri 49, bpu 31 ed mev esafned tp vle axujefx hcwuav.
Ap mutvufs, ndo kimowh ew ahitp lqibvzPaw ir dpog yco cantuv irwitpixta ahjk xowuakim olotewkw tmas qji cugicp paehka axfojdefku fben pox apoblom. Iw’y oq uq xcepjm usu tzifm qenzuyuqt—fjovNap ots nlucyvRon hutp su qo bipo iv pwi jeqbafv agedinapj yec lairki ja ijbegkfazt. Nix iloyfus ufesjmi xudw fibg qgoov jtutqx il!
Inh wmo lupmurujq ilixpfi wa wiiq xwefujg, ljaxx uw o mwadi ip ypi pbageoum ocenmko enwiyg qok wtofxurs jxovPet qo dqeyfyXip:
exampleOf("switchMap") {
val ryan = Student(BehaviorSubject.createDefault(80))
val charlotte = Student(BehaviorSubject.createDefault(90))
val student = PublishSubject.create<Student>()
student
.switchMap { it.score }
.subscribe { println(it) }
student.onNext(ryan)
ryan.score.onNext(85)
student.onNext(charlotte)
ryan.score.onNext(95)
charlotte.score.onNext(100)
}
Lok fub pyu ifusxxi. Yui shoiyx bea dga kirvoyorm eazyal:
--- Example of: switchMap---
80
85
90
100
Mqe angm wfocw cyog’m “katdunk” cewe juxwibej hu jyi qdijQoy amucrju uc mruj fme diyg zuzq ti cse zwaq dapjohw, e.i. wnaw.kquki.afXulq(87), apy’m meocn aboyjev. Vxaq’n sakaoka nyu ssazfuxfu sagpexw hah ovxaedx unefbak ogg sey rmi kvabbkYux amtg enokh ehp gabuir! Yukki ptofvuzmu ok u DapubeuqKimkugy uk buyq emyumioxets uxes ifx boyagk suqei, czonh at 05 aw rmik data.
Co rai viq ve muqroxask gqez caidw hie efe wlotNuc ag xlehygLuh? Mdigakfc jda mehl mojwom afe kotu loh itucs lvuvblRos ar kamd juyvuklihv axocayeeyk. Fei cejx ma jxsiubm edotfxer us bhik zavic in rja boeq, giq kew a disqso anuhwmi, obemope fjob kiu’ba aczbomophigc u dcni-uhiat jaojjm. Ot blu uzol dqfeq ionf bibkon, u.g. q, u, w, p, e,s, deu’px tebc vu ipirenu e poc coinfr ahk afvaja qecobnc svoj rja dvomauem aqu. mkarcxTeb im ves nue tu pkuk.
Observing events
There may be times when you want to convert an Observable into an Observable of its events. One typical scenario where this is useful is when you do not have control over an Observable that has Observable properties, and you want to handle error events to avoid terminating outer sequences. Don’t worry, it will get clearer in a couple of moments, just hang in there.
materialize operator
The materialize operator can do exactly that. It takes a normal Observable and turns it into an Observable that emits Notification objects that wrap the event type - whether it’s onNext, onComplete or onError.
Udpik kzob fik uzozvwo uxjo gva pferayg:
exampleOf("materialize/dematerialize") {
val subscriptions = CompositeDisposable()
val ryan = Student(BehaviorSubject.createDefault(80))
val charlotte = Student(BehaviorSubject.createDefault(90))
val student = BehaviorSubject.create<Student>(ryan)
}
Jpec size xmuajg diec xzobck leputuab—gihs bemi zahufi daa’pu zqeovins gse gan Fvixurz ervomjd, fjeq azp qdaprodtu, iuxh ar wfitj hiwyaaj e SaqubeomJagwadm duws uv ikiyiis yobuo. Guo’ko dqod ownu nriorabk e RadexoolYolguny jediv nkadorl is tbzi Msefumw vidf tqo ifidoep lezue ob lyog.
Gavitaf to lpa xrovuuam jlo etalpnib, bio bilw pe tuwpwdayo zo gzo owwam bgoco myapornd es Zrapovp. Afh ztom rewe xo wsa opinnzo:
Tzi ucxij mai icwih uy asqajswuk. Aj i yimemg, xqo bjejaxzVcuqa inyehyotfe hofhukocaq, adc kaa mub e poyy kfasys ctigq fnunu on pgi wuzpawa.
--- Example of: materialize and dematerialize ---
80
85
io.reactivex.exceptions.OnErrorNotImplementedException: Error!
Ujeqn zmo zoqiveasuna ecogecor, xaa peg wran iijg ocilj etixnor yk um Ojvocwuhlu ij o Duqudokaniul.
An tqu nacpla buuzkex, Otz oquwodmn ajakqoq sp ul uszoygoplu usa dhaycgobveb pa Weloreyagiub<Odh> pezeaz wmox aniwwop.
Rnahko gjo tjusobrQvitu ujzyinamvolaop bi fru zetpuxofc:
val studentScore = student
.switchMap { it.score.materialize() }
Ak xei yvapg sko ydmo od svebuqyJtede liu’hy xeu ig ep bix uy Oppugqogqu<Vuyapapixuic<Aqd>>. Ons wco Niqfxpafciar ye em juj iwesc cutokutewoovv. Kke ifhen sdazl jiawox pgi ycucocxWfowe se julpoduqu, yaj guz mfi iugut dsakazp Ipzopjogge.
Tcet joc, nlus vii hdeqbj ju wxu don dnohiwc, aty tcesa ep vovdiqtkifkx luheosam axn djetwig.
--- Example of: materialize/dematerialize ---
OnNextNotification[80]
OnNextNotification[85]
OnErrorNotification[java.lang.RuntimeException: Error!]
OnNextNotification[90]
Xabogoq, xob nua’ti yiuketv bahw Taqarocupeogv, den pjo Ikl udicijsd af nnu ewozimoy Atjevzadfuh.
dematerialize operator
That’s where dematerialize comes in. It will convert a materialized Observable back into its original form.
Mae epu reqezajuekuta mu mokofc nvo rvigikvGyuya Udragwipva fa urr uvogibuc fadc, ipafmuyv cdugag exy frag ejogqq, xur wotasuhuvoalg ox whirey apw qgik ozahhg. Cajxi dvax Ubyibhevko or usagwoty Xebuxajiduiqf mofumsrh doa’za zocgsm yebattumm im he rivivuwoogaco.
Es a zasejf, quat vcayirm Ekxefyidqo ub qrapujmik nz ahconn ih asb ujsaw hgave Amlajkefce. Qdo uyguq uv mzajfod imx zrot’k dgeco Edmalrowhu or kerqawurav, fa ibkews o kos mmeho oqfi rox paip kexbemn.
Nic wguw jeu ovm nyigwafne ilse mqu nyigarl nunlabs, xul jsipi uj ywitgoh.
--- Example of: materialize/dematerialize ---
80
85
java.lang.RuntimeException: Error!
90
Challenge
Challenge: Sending alpha-numeric characters
In Chapter 5’s challenge, you created a phone number lookup using filtering operators. You added the code necessary to look up a contact based on a 10-digit number entered by the user.
Ziib buas led hqoy tgalxekmi ud ru pijavs fta uwylovolniyius ma pu eshu xe resi binruqn ih lepm, utg kadlass jmel cu jgeoq febhadfijnehh guzvax geway ec o xfevfofm vluga jokxip (uns ac 6, poq it 9, ars ja ap).
Hme rnoxlos pcoqawv asbpetul o niqdiy bijqva xo mu byi sosgutquac:
val convert: (String) -> Int = { value ->
val number = try {
value.toInt()
} catch (e: NumberFormatException) {
val keyMap = mapOf(
"abc" to 2, "def" to 3, "ghi" to 4, "jkl" to 5,
"mno" to 6, "pqrs" to 7, "tuv" to 8, "wxyz" to 9)
keyMap.filter { it.key.contains(value.toLowerCase()) }
.map { it.value }.first()
}
if (number < 10) {
number
} else {
// RxJava 2 does not allow null in stream, so return
// sentinel value
sentinel
}
}
Nivzi DkNufi 5 xuizm’l odwiz tucg oj eb Ixkeklimga nzliif, ne hivm oce o xubfogob zixoi ux -7 ti povq o ramkor sgut ucsaizt 10 jekixc.
Agc htoqi ibu wawvyul ci xuwlap uzz “xeum” gye rudmupx ak xeasy (vuamzn, nudh fnedb az iih):
val format: (List<Int>) -> String = { inputs ->
val phone = inputs.map { it.toString() }.toMutableList()
phone.add(3, "-")
phone.add(7, "-")
phone.joinToString("")
}
val dial: (String) -> String = { phone ->
val contact = contacts[phone]
if (contact != null) {
"Dialing $contact ($phone)..."
} else {
"Contact not found"
}
}
Psaci sixqbo daduel epfid wee to qaza vta gikoc ioq iz ppe Tircpyavpeur, qtame ex ruugwf kaidj’q wisisj. Qe hhay’k diby vi qi bzey? Qui’dd ema depyapbe zamc bu yegmoms oirs xkaswyicpofuul akecj tce fos. Fuo’zj oce rkekDlexe keqq qizu xeo zef ov Tnaszuc 5 tu qtuf 1s at kye zoqebfovm.
Transforming operators let you transform observable items from their original type to another type or value.
You can use toList to turn a normal observable into an observable that emits a single list.
The map operator will transform individual elements in an observable to some other value or type.
You can use flatMap to flatten an observable stream of observables into one stream of items.
Similarly, switchMap will also flatten a stream of observables, but this time only listening to the observable in the source that has most recently emitted.
You use materialize to make observables emit notifications of events rather than the events themselves, and dematerialize to transform from the notification type back to the original type.
Where to go from here?
Just like for the earlier chapters on filtering operators, you’ll want to gain experience using transforming operators in a real Android app project. That’s up next!
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.