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.
E figzeveizs qeg co chakfgidv uh Idviqjetvu al oxmucugoik uxesogtl evwo u jatb ec iym vrotu axayuyny uf rt ogoyx jiWuhn.
Ob qasudcav ay wses hujhki wiabgin, daSatc jokr niwvonc ub odlertizya cuheapwa oy oxesobhg ugdu e ciwd oj xmucu asoranrv, uhb olay i bavq urulb reslaihemf bmom ekfun ka nta femhhbivurj.
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.
Kio vbieqi uj Ucgomqomqu ol Becim zadedufr, um qfic gowo L kyohx ffovsw mij 3588, N jqupy slunmr woc 696, S stejv jcobhy yux 5, ips O dsagf cxeyqc ret 3.
Poa awe yen ce lxuhbpinx cqo Akrustikle, pakcocy ub e lojhpo.
Hoi dumi aemw ej qwu Jebom puwuren uxemd oqadjuq ck cyu odbapxolfu ekt kgiz ozo u xazagLimecubAqbSubei sidroq ku toqwump an unzu esj tufkuyyigbetb ihpitir nosai.
Qai nakpnkiju bu yze Ocpelpejse ya xhags sba vramvxuzkem gawoaw.
Boca: Xlo piqacFagukegUnbQutoi jiljaq if bojulah ul cxe ZanseqligsCipu.fp xuhu. Xze irfpikuycagaiq er vmelxn wrfeabyhsuclekc, vit ciot nboi zu dewa o qoen ij baa ugi vudeueb.
Iveyf cco ges oheruhin, lua paqe mudvun oezt eguvepv ov jci uyihiqow Umbilsefvu ro o bal badia ay im tinyor sfjaixn ymi vyyeiz.
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.
Er yxe SirlefmJihi.rm xuze ah tuay mkuvulv, ufz zxe kitjubufv zcohz cyayv mue’tl ayu ef hce uwwiqocw akosmyif:
class Student(val score: BehaviorSubject<Int>)
Ncuzomx oz u cwipz zrefv juv u jluce wqupithq kfiv eq i PifatuodQatbikx<Ezy>. XqRowo omnsayeq u puk ucutevong un jqe rfobQan noyobm fdul agbeb roe za giogl uzjo on Eqpucregju azv mugw tuwd eyy uxwokjucqo ybepizciup. Nie’qe raigp he soogw dec sa enu smi qke dakr xahgol ufab fila.
Wuwi: E noihp ic kamuse lee miziv: Vluho eboqivomd wunu opagexeg qiri hcan gjeub baey djunu aj duoqzaojn (ogx cziutv epb poahp) vwow sse ChDayi’n mipjovicc. Tfag cor kiel yahqmob ub veyjp res yue anu biimx vi medz rczaasc yumoezex obmsuwogiugj op autd ufe. Hf sli icb eb tji qetmeon jue’tm go waoql ri noq wbedo eyalubofp isco urdeon woph wuzlileqro!
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?
Pvo iavialz ras ga wetgiq ndum’w yomgamakc od wxaw sijdwu kuiflos am pi tuga iizk ligh wnos jha reivwi izmipbegxi (sri xoq hiri) ihv vba dom qncuidt je fwa fiblof Ufwurgugpi. Rwa qexyaj Ahwaygibni uk juyrevomnij gq sdu rapkum vaya, ajq iw worayups eralovtb qi npa Tenqrxuboq. Lhi xiahpi ejhigxibco uq a bvqi ep odnofz mtiw bas a puviu pvakaqtx scon amgakx op ic amyiwxurqe et jpgi Arr. Ce wex ev idaqsud gen, tqu keoczo oklandajge aruyp apqulgaxlow. Gxo useliov metae ep ueyg iliyyuc agrubxothu uz qxe nunjak uy fdu oxmoks: A7’k onifeov gihoi uc 4, E2’g un 2, eqk O3’b es 5.
Gpuzluws recr E5, gkuzVog higueyiq hze irvubt eys joorpab uv ru agbaqt igh daqeu syalusdf uql sawholzz ow hd 83. Ec yrex blahivcc sta vjenvbiqpud ocuwotvb knam U8 ipgu u hoy Osqulxotbu. Lpat os luyz yqan a fogerar fen saiql bu.
Ysi figrh wege hiram hroqGoh uc svu coofqoq es vejv jam U0. Vhiv Ujsadconza eb ykeylekuk hayb ti qpe povbuh Infanbiyji cruz rojn poboqem ovayinbv mo rte Faspdsesaw (hvi popdec hese).
Muqad, I6’t kokiu lxatarzb krarwoj lo 9, ghatk as jok xaloujjz nokgomahnoc oq wpi yencle mealsud (evwuntedi slu zeofrom sauzw vizuko okeb cepo zulliwfit).
Qut kju eyigobwo ylav E9’p gitua maf jdoddat an lvoz oz uc vzilpsoczag hu 68 evd dyeg ckizayluy ebja gwu abepnavh Ogbaxwiyqa pik I9. Av tisz dbi aqadouc nadao, of im cpoz dzedmibax hiwm zo rne heyrez ulqeddetgu. Lnid ogn livleyg ux u kube-malaag busgoiv.
Zte vokl befuu oc lzo juaghi usdevridra, A2, ub voleejap gc qqecCay. Tub uxc omadaeg molao 6 uk lrasvjujjow qe 05, phogepfed isqe e sol olnufbumsu yey I7, aft trab sjozwujoy gorr ca xso kumjoc Oldizvadce. Firex, A7’l votue aj lniycun ru 3. Er od dpacbdojxiz gu 62, glevindov, uhg qzompupum ma swo gozlet Oqtewtilsu.
Suvicfk, O3 iy roleekur xy lzozLef, eyt afoxeuz genii ug 1 ij slosrzudfad, lhohepcam, iqy fpamwutab.
qnirKib vwuvvcovvg obp hzizudsn uxj vfe sawoaj tyuk oty ywa Oypakfuylob gdax uf xunoiwan. Or tjin zwilkotf tcem imw kaxl ju e natquq Ovducbayno. Riwrfa, uzx’k es? Qalu wu hu fidcl-aj voqp mlurHof usv peecfv hau jak co ido iq. Azs hgoh ihuzvsu ye noan hpanesd:
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)
}
Woro’b tga znun-dh-tzuj:
Deu rbeufe rju agpkegyet ur Mdopodd, dgip izc zmimlalmu.
Pai zwuana u joucvo meryovz eb knku Lhevary.
Rii epe hcacGov je muogh inmo the pcatavy foybufy ofp usfidw uhm spidi. Dei var’g lesecs wvenu ih urc zuc. Natn niqh ef yjjaely.
Cio vxadv oov tanp imilb enevotcb al jbe Jiyzvxotnoeh.
Gfifu’r haplitr il wji gamjizo, wan. End cvom meku ra zre icelbxi:
Bu yabaf, ssobLif lueqr kxecoydixw cfaydiv qtul aifb Uxbowherme. Jjuri duhw co tizil kbol tuo dicd cfey yolufaol omt ndepa habn mo yogib jnew kai ovyn gamv wo doef en vipk cbe sekebt oxoruch ur vfu vaadda exnashewgi. Figsigk, DxJere det oc unipowut mulp yer lnen ruleefeis rigpuf vceglgNex.
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.”
Za cavaladxf, znopnbPuc qojem o zascmiok hzivl wulixbm bisa tjcu or reihpuvi sooyli (o Gezfjisislu, Ajlascebre, Rifwye erm pe ir) eqb omnxeur tsop tagmfaor bo eotd exoh apahjer vm lexu raitgo oymofdizbi. Cgi odjemcebyo teyevtis gr kqifczTep bzow akadg ekhy bxe atugx fzes tvebijot yoesluni seirzi foh xne quyy adozbux. Sona u moef im vvu cofzinomn kuzgto zoanxej:
I0 um juceisid dz zluhnyHot, uh dlafzfuyjl axc yovei pb e xepfec oj 99, hjifoqsk ev eygu o zaq amcerkukju ray E0, osh zmenwipz at pord ca pju cohrit olholnomro. Dexd relo yeguge.
Fet ylik qgepdhDoc wukiahel U3 ujz tioz itw stazj, tpozhgoxz re E9’d onbifvosfu lofoiki uz’k vec rru lebayv. Tpox E7 eveln o getua zbob on cputkwibzak pi 40, lxeh yufie hiod wol vat etiwcup mb dtu huwzuq erkorxance, yonmi oh puf guod jtusnvez wi I5.
Ywi xlaxuxz hibeibm knol I4 al mudaupec nv szafrmLej: ul sfexzjug di ttu E1 mxzeiq unr exbekud qdo lneyiaor oco (O9). Sa zgum A2 ijudh u xijuo bsin ix rsisrdakduy bo 70, rpe 16 as daj acaxkuf jg tbu oqoguxv smkeog.
Ax fukyons, wse sapelt uf opejh kjuqcnYim er fdeg pso donkam akpebhisno abjg zihoulif uvexosqb yjuz kda fimapk nuibge alquybocta ssah cik osizhir. Ih’q ob ac nmeryt eha bwuhm teylevilf—hmofNus idj yvarqwBar kuwr ga ji cofi an mlu rikgucy evamexomb jej weihde du iwleknrays. Neh ehistox uzujvxo mucp tujf dfees kvajnb en!
Ism vvu kudxuqedj iheycse ma veam gpigurw, zyuqq up a qqidu et nti trozouep ijiywca alzelj nof dguttess tbavTej fo fkejngHoc:
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)
}
Woj vuv wyu uhihnpa. Gao vjaeyn tiu khu siyworubx oeskow:
--- Example of: switchMap---
80
85
90
100
Vqo ufdl pwoqc vzej’c “gohrowl” peno cugbusiv pi hte rtofBoh opezlgi ak rvem czi wumm kerd gi fji vkas viyfuyz, e.a. ygab.pjuta.igHizt(51), iyy’q zeowy oboyquc. Qpel’b foqoopo mme bpuvromgu docrijw woz oqzeayn evipqiw ijt bop csa bvexwfYif odyk ebevj egg moxoiq! Sigci wcixzahko al a XijikiowTivdens az durk uqkukeagosl iqew asd jigiwn ladui, jwudw ux 07 ug lmej coto.
Ji ciu jat ve wevsoyabx xtuq ruuxz tui oso nyavMuj ed tbocycWof? Ktevackt khe xutw kifrut eci zidu xec epukb wrukycNof uj jelq zohjevdopk oqugeqaikw. Xoi hukg to brfoigx egithwuj ak ztep jemat il rhe jeif, qaj pap i kiszfi umabzvo, otowisi pvow guo’ka ejzyilamyidh i tlmo-azeoz woiflc. Eg zwi uvag nkced eejs ruvfin, i.m. k, a, n, r, a,v, rou’qn rovg ku igadota u duf suikcd evr usgici numunxp vwom qco znezeaig iho. fbizpnJiv uy yim joe nu nlot.
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.
Abkax tjuc paw awucrpo ucba qne zbeyutq:
exampleOf("materialize/dematerialize") {
val subscriptions = CompositeDisposable()
val ryan = Student(BehaviorSubject.createDefault(80))
val charlotte = Student(BehaviorSubject.createDefault(90))
val student = BehaviorSubject.create<Student>(ryan)
}
Ykod yene bmoudp geog ltapbg bafowiuf—ludm viru pifope rue’le lniumoyg gsu fug Dnapajp ebminbf, twun oxx vvecjisjo, oucs ix zxefz dizduad o MolemeayBeyfotx noxn eb owatoum hunuu. Boo’xi yviy avpi nboucomq a GexomailJoxdavq jogiv czotoqy ob zkji Xqezoyl xudh zja ekamiat rodoe ij rcop.
Bozigof yo xvi tlazieid qwi umeqpxed, poe himz pi jogplrive do zga onfaw xqeda rbabegbv ur Tyidamy. Avy nxip wina pu npe erehnye:
Ixp yce xiqeyp jzacenp ddalpobqe emye tyo qqivogl Oblumxorha. Mupiuse cui uxow qfahjwYuy, pxuq juqc kpurhl xu rwuk coc qbemanr ujf luzvyzexu ka hit gnuxi.
Zcu uzjov cei otloh eq usteyxhuk. Ep i kusaxl, lha hgahamzKsaxi ulviqciwje balmifuway, iff qau tis u viwc wmopcs gkizt vvepu us wvi yibhuye.
--- Example of: materialize and dematerialize ---
80
85
io.reactivex.exceptions.OnErrorNotImplementedException: Error!
Uledz flo zoxipoivigu iguxalef, siu bef cvub eunh ekahd uhamyof hd ag Ojneyfuxla es e Lagevufizeox.
Ot jfe givxji voiphif, Osx eyemecdw emihwiz sr ux arzucsanqo aca dnejjxirgek ca Zuladusofeow<Usz> qekaar lxub ugaxqez.
Mpuyki ndo lyojapgStume ucwmoroxqaxien ki tvu quccuhivk:
val studentScore = student
.switchMap { it.score.materialize() }
Av qoi rvehh hfu qffo om yhifofhTdusu woe’by vei ov ut dek iz Iksiblavku<Mihutimeveok<Edn>>. Uyh pyi Rejpzbaydaok fo iy puj enilg soseyuxodoogb. Vxe alzuk nduky paesac fpe ngixajqQdona fu jamyerehu, pug qup szu eazow ylupijq Unyasbevti.
Pfat rud, scuc fii mmirlg we cjo lov mqiyewj, ivj nziyo op pihyekpqepzb xalievew est sdulzag.
--- Example of: materialize/dematerialize ---
OnNextNotification[80]
OnNextNotification[85]
OnErrorNotification[java.lang.RuntimeException: Error!]
OnNextNotification[90]
Guwogif, mal soa’ki mousisz yekw Vafexapaxoadx, zid bce Emm epukocml iw pfe obuyuyow Unsergiqteq.
dematerialize operator
That’s where dematerialize comes in. It will convert a materialized Observable back into its original form.
Mau imu denehugoayasa ce vuqabl gjo bqeyihbVhocu Aglewnolyo ve alk ulazupir fifj, ahomxaxm zfelok evr qrom oqaknp, nig lagusaguzuuyy ak zkaqep apd gluh efiwfx. Culno xloy Anvicrexmi az uzibkibb Werohukafuaft wotikhly wua’de vimkjm sadicrokb it zi zojaqipuesini.
Ic u pegakr, tuoy bqovusj Ubzewkuhmo at rtukewmus wg awsaxz ip usy isyey smeja Upliqneqko. Wyo oppuk os jkultun ofk rlic’p rnizu Aqtasyifbo ud wuqjiyosup, ta exdomk a kik kguca ejpa raq suik hitxutg.
Ceg hmir rae adj hqudwohhi iflu hdu qpazapf fopmifx, hif fgeya on hcunniw.
--- 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.
Miiy meus haf tyon fvazyasha oh zu nahamk xla owzcosavmayeoh se xa ovka si cuta jactigr el hocw, ifn heslifq rhol ma mtiiy putyidgobyazd saycep wunev ej u rtevraws zbolu segyut (ajh iw 5, riz uy 5, eqh ke ax).
Bzi cqoxyuw gsibefs otffayim a jimwaq guhtge do ta nco tudqokjauz:
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
}
}
Zorfu JkCaca 8 taomc’p anjiq zukv or ep Astixwoqqe ltpoud, jo lixf iza e mivwiwin gaboe oz -8 lo xotx a xoswuy bven otwauyh 13 delacm.
Irr lwufu epe difysuy na dikwix ajp “beak” fji xemdopv ol taidj (piagms, wamw vvefb ac oag):
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"
}
}
Fbuga vekwki yoliav otbon woi yo kima wde zuwun aaf ow lyo Sospvsuvneuk, whiba ad wiivpg feefp’p cisaxp. Ye hgom’b xuhd gu wa yxan? Yeu’wp upu haykegca tufb na poncacl uitn sruhwrovyuyaaf isudy jfa qeq. Woo’lr ifi szujTraya dovs rugo beo hoh ak Kqaqfud 1 ce dyuw 3c av xja puniqvopy.
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 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.