Cem
Cem

Reputation: 158

RxJava using toList after flatMap fails as flatMap isn't complete

I have three objects (say A,B,C) and to get C I need B, and to get A, I need B. In screen I need to display a property of A together with a property of C. Even though I can get all necessary data, since I use flatMap which do not have onComplete, toList() does not get executed. Here is my code.

For every a in List I need to get c and I need to return a list of type ResultMode which includes properties of a and c.

override fun methodICall(): LiveData<MutableList<ResultModel>> {
        return mySdk
                .getAllA() //Returns Flowable<List<A>>
                .flatMap { Flowable.fromIterable(it) }
                .flatMap { helperMethod(it) }
                .toList() // Does not get executed as flatMap isnt completed
                .toFlowable()
                .onErrorReturn { Collections.emptyList() }
                .subscribeOn(Schedulers.io())
                .to { LiveDataReactiveStreams.fromPublisher(it) }
    }

    private fun helperMethod(a:A): Flowable<ResultModel> {
        return mySdk
                .getB(a.propertyOne!!) // Returns Single<B>
                .flatMap { mySdk.getC(it.property!!) } // get C returns Single<C>
                .map {
                    ResultModel(name= a.name,
                            date = c.date.toString(), 
                            message = it.messageId!!
                         )
                }.toFlowable()
    }

Note: I asked a similar question earlier today but it did not require using flatmap more than once. You can view my solution to that in this link

RxJava - Mapping a result of list to another list

My Effort (Which is probably wrong) Here is my effort of transforming first method (for second method I just remove to Flowable and return single) but it has a long way to go and I think I am in the wrong path.

 override fun methodICall(): LiveData<MutableList<ResultModel>> {
        return mySdk
                .getAllA()
                .concatMapSingle { Flowable.fromIterable(it)
                        .map { helperMethod(it) }
                        .toList()
                }
                .onErrorReturn { Collections.emptyList() }
                .subscribeOn(Schedulers.io())
                .to { LiveDataReactiveStreams.fromPublisher(it) // here it is single. I think it is because two maps are applied both to helperMethod itself and inside helper method to result model}
    }

Upvotes: 1

Views: 1483

Answers (2)

Cem
Cem

Reputation: 158

Edit : I found another solution

 override fun methodICall(): LiveData<MutableList<ResultModel>> {
        return mySdk
                .getAllA()
                .concatMapSingle {
                    Flowable.fromIterable(it)
                            .flatMap { a ->
                                mySdk.getB(a.propertyOfA!!)
                                        .flatMap { b -> chatbotSdk.getC(b.propertyOfB!!) }
                                        .map { it ->

                                            ResultModel(name = a.name,
              message = it.body!!)
              }.toFlowable() 
                            } .toList()    }
                .onErrorReturn { Collections.emptyList() }
                .subscribeOn(Schedulers.io())
                .to { LiveDataReactiveStreams.fromPublisher(it) }
    }

Original Solution

This is my solution but I think this solution is really messy and it can be improved a lot.

  data class AtoBDTO(var name: String, var b: Flowable<B>) // I wanted to map one object to more than one so I created this. Probably there is a way to do it with rx functions.
    data class BtoCDTO(var name: String, var c: Flowable<C>)



    override fun methodICall(): LiveData<MutableList<ResultModel>> {
            return mySdk
                    .getAllA() // Returns Flowable<List<A>>
                    .concatMapSingle {
                        Flowable.fromIterable(it)
                                .map { AtoBDTO(it.name!!,    
 mySdk.getB(it.propertyOfA!!).toFlowable()) }  //getB returns Single B
                                .toList()
                    }
                    .concatMapSingle {
                        Flowable.fromIterable(it)
                                .map {
                                    BtoCDTO(it.name,
                                            it.b.concatMapSingle { mySdk.getC(it.propertyOfB!!) }) // getC returns Single C
                                }
                                .toList()
                    }
                    .concatMapSingle {
                        Flowable.fromIterable(it)
                                .map {
                                    ResultModel(name = it.name,
                                            message = it.c.blockingFirst().body!!) // I use blocking first because otherwise I can't get rid of flowable
                                }.toList()
                    }
                    .onErrorReturn { Collections.emptyList() }
                    .subscribeOn(Schedulers.io())
                    .to { LiveDataReactiveStreams.fromPublisher(it) }
        }

Upvotes: 1

Bob Dalgleish
Bob Dalgleish

Reputation: 8227

There doesn't seem to be a good reason to continually deconstruct and reconstruct lists. Assuming there isn't:

override fun methodICall(): LiveData<MutableList<ResultModel>> {
        return mySdk
                .getAllA() // Returns Flowable<List<A>>
                .flatMapIterable(it)
                .concatMapSingle( item => {
                  mySdk.getB(item.propertyOfA!!)
                    .flatMap( bItem => mySdk.getC( bItem.propertyOfB!! ) )
                    .map( ResultModel( name=item.name, message=it.body!! ) )
                 })
                .toList()
                .onErrorReturn { Collections.emptyList() }
                .subscribeOn(Schedulers.io())
                .to { LiveDataReactiveStreams.fromPublisher(it) }
    }

Because the concatMapSingle() operator knows about each item, its name can be known when it is time to construct the ResultModel. Now, you no longer need to tear things apart so often.

Upvotes: 1

Related Questions