RxJava Conditional FlatMap Cleaner Way

by Tenten Ponce   Last Updated April 15, 2019 10:26 AM

I'm looking for a cleaner way on how to create a conditional flatMap(), I've read this but I'm having trouble applying it on my code:

// given variables for the sake of simplicity
val stringSingle = Single.just("dog")
val isCatEat = Single.just(true)
val feedCat = Single.just(true)

// example
stringSingle
   .flatMap { string -> 
      if (string == "cat") {
         [email protected] isCatEat()
             .flatMap { isCatEat ->
                if (isCatEat) { // if cat already ate, proceed immediately
                    Single.fromCallable { true }
                } else { // if not, feed cat
                   feedCat()
                }
             }
      } else {
         Single.fromCallable { false }
      }
   }

as you can see (well, the code is very ugly, nesting ugh), I want to avoid calling the feedCat() by checking it first if the cat already ate. I'm having trouble applying compose() function as I can't reproduce my condition.



Answers 3


You can use filter, which will only emit if the predicate is satisfied.

I've assumed you want to know when this cat needs fed. Therefore, I think an observable would be more suitable:

private val hasCatEaten = Single.just(true)

fun feedCat(animal: String): Observable<Unit> =
        Observable.just(animal)
                .filter { it == "cat" }
                .flatMapSingle { hasCatEaten }
                .filter { !it }
                .map { Unit }

fun observeFeedCat() {
    feedCat("cat")
            .subscribeOn(schedulers.ioScheduler())
            .observeOn(schedulers.mainScheduler())
            .subscribeBy(onNext = { // Called when cat needs to be fed })
            .addTo(disposables)
}

UPDATE

This is a better solution, which handles both cases:

    fun shouldFeed(animal: String): Single<Boolean> =
        Single.fromCallable { animal }
                .filter { it == "cat" }
                .flatMap { Maybe.fromCallable { !hasEaten } }
                .toSingle(false)

I unit tested this code (not cat, cat has eaten and cat not eaten) so Im pretty confident with this answer.

Iain
Iain
April 11, 2019 14:19 PM

I'd extracted the second if into extended function

// given variables for the sake of simplicity
val stringSingle = Single.just("dog")
val isCatEat = Single.just(true)
val feedCat = Single.just(true)

// example
stringSingle
        .flatMap { string ->
            if (string == "cat") {
                isCatEat.flatMapIfTrue { feedCat }
            } else {
                Single.fromCallable { false }
            }
        }

Where:

fun Single<Boolean>.flatMapIfTrue(mapper: (Boolean) -> Single<Boolean>): Single<Boolean> =
        this.flatMapIf({ it }, mapper)

fun Single<Boolean>.flatMapIfFalse(mapper: (Boolean) -> Single<Boolean>): Single<Boolean> =
        this.flatMapIf({ !it }, mapper)

fun <T> Single<T>.flatMapIf(conditions: (T) -> Boolean, mapper: (T) -> Single<T>): Single<T> =
        this.flatMap {
            if (conditions(it)) mapper(it)
            else Single.just(it)
        }

I provided 3 funs so you can reuse it in other places

Boris Safonov
Boris Safonov
April 11, 2019 14:39 PM

I deleted my other answer as it was not what you wanted. Didn't see you wanted to feed cat in the stream. To do some RX stuff based on filter switchIfEmpty should work for you.

    val stringSingle = Single.just("dog")
    val isCatEat = Single.just(true)
    val feedCat = Single.just(true)

    Singles.zip(stringSingle.map { it == "cat" }, isCatEat) {
        isCat, hasCatEaten -> isCat && !hasCatEaten }
            .filter { !it }
            .switchIfEmpty ( feedCat )
            .subscribeBy(
                    onSuccess = { /* true for it was fed false for it was either not a cat or already fed */ }
            )
Stuart Campbell
Stuart Campbell
April 15, 2019 10:25 AM

Related Questions


Updated March 31, 2017 04:26 AM

Updated December 21, 2018 22:26 PM

Updated November 12, 2017 01:26 AM

Updated February 10, 2018 09:26 AM

Updated October 18, 2017 10:26 AM