RxJava - Mapping a result of list to another list - android

I want to convert every object in my list to an another object. But in doing so my code stucks in converting them back to List
override fun myFunction(): LiveData<MutableList<MyModel>> {
return mySdk
.getAllElements() // Returns Flowable<List<CustomObject>>
.flatMap { Flowable.fromIterable(it) }
.map {MyModel(it.name!!, it.phoneNumber!!) }
.toList() //Debugger does not enter here
.toFlowable()
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) }
}
Everything is fine until mapping. But debugger does not even stop at toList or any other below toList. How can I solve this?

Using flatMap() you'll only flatten the Flowable of lists to a single Flowable of the elements. Calling toList() on it requires the Flowable to complete and therefore you'll most likely never get there. If you only want to map the elements in the list and have an item with the new list emitted, you should do the mapping within flatMap() or maybe try using concatMap() to keep the order:
...
.concatMapSingle { list ->
Observable.fromIterable(list).map {
MyModel(it.name!!, it.phoneNumber!!)
}.toList()
}
...

Here is my solution to this. Thanks to Tim for leading me to right answer.
override fun myFunction(): LiveData<MutableList<MyModel>> {
return mySdk
.getAllElements() // Returns Flowable<List<CustomObject>>
.flatMapSingle { Observable.fromIterable(it).map { MyModel(it.name!!, it.phoneNumber!!) }.toList() }
.toFlowable()
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) }
}

Related

Chaining and mapping kotlin flow

I'm trying to get a result from a flow, that retrieves a list from a room database, and then trying to map the list with another flow inside from another database operation, but I don't know if it is possible and if it is, how to make it, at this time I'm trying to make something like this
fun retrieveOperationsWithDues(client: Long): Flow<List<ItemOperationWithDues>> {
return database.operationsDao.getOperationCliente(client)
.flatMapMerge {
flow<List<ItemOperationWithDues>> {
it.map { itemOperation ->
database.duesDao.cuotasFromOperation(client, itemOperation.id).collectLatest { listDues ->
itemOperation.toItemOperationWithDues(listDues)
}
}
}
}
}
but looks like is not retrieving anything from the collect. Thanks in advice for any help
I think you don't need to use flow builder in flatMapMerge block. For each itemOperation you can call the cuotasFromOperatio() function from the Dao, which returns Flow and use combine() to combine retrieved flows:
fun retrieveOperationsWithDues(client: Long): Flow<List<ItemOperationWithDues>> {
return database.operationsDao.getOperationCliente(client)
.flatMapMerge {
val flows = it.map { itemOperation ->
database.duesDao.cuotasFromOperation(client, itemOperation.id).map { listDues ->
itemOperation.toItemOperationWithDues(listDues)
}
}
combine(flows) { flowArray -> flowArray.toList() }
}
}

My doOnComplete is called before .Map finsihes

I have a problem with kotlin observables, I have searched through internet and stackoverflow but I think I am missing something in concepts. I have dashboardRepository which has method called getCallsCountForWeek, this basically returns flowable list for last 7 days and now I need to iterate through all flowables and then update my graph with count of calls user made for that day. Here is my code
fun getCallsCountForWeek(calendar: Calendar) : List<Flowable<Float>> {
val result = ArrayList<Flowable<Float>>()
for(index in 0..6) {
calendar.add(Calendar.DAY_OF_MONTH, -index)
result.add(dashbordDao.getCallsCountForDay(customSharedPreferences.getUser()?.id!!, CustomDateTimeUtil.getStartOfDay(calendar), CustomDateTimeUtil.getEndOfDay(calendar)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()))
}
return result
}
Observable.fromArray(dashboardRepository
.getCallsCountForWeek(calendar). map {
items -> kotlin.run {
items.forEach {
it.subscribe({
Log.e("Result", " Count: " + it)
},{
Log.e("Error", "" + it)
})
}
}
}.doOnComplete {
//We will do this when it is completed
Log.e("Result", "Completed")
}.doFinally {
Log.e("Result", "Finally")
}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe()
The problem is that doFinally and doOnComplete are called before map completes its iteration through all the flowables. I tried to use .zip for flowables but apparently could not make it work too.
According to other posts on stack overflow, doOnComplete is called when subscription is successful but I want that to happen after everything is done inside .map.
You should use flatMap or flatMapIterable instead of map and have only one subscribe call
Observable.fromArray(dashboardRepository
.getCallsCountForWeek(calendar)
.flatMapIterable { it } // iterate over list
.flatMap { it } // use flowables from list
.doOnNext { /* do something with every item */ }
.doOnComplete {
//We will do this when it is completed
Log.e("Result", "Completed")
}
.doFinally {
Log.e("Result", "Finally")
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.ignoreElements () // if you already handled everything in the doOnNext
.subscribe()
After looking into answer from Eugene Popovich. I was pointed into right direction and then I did the following and it worked.
So, first thing, I modified my function to return list of Single Observable instead of Flowable as that any who made ore sense. Once done I did following as suggested by Eugene but just using flatMapSingle instead of flatMap.
Observable.fromArray(dashboardRepository.getCallsCountForWeek(calendar))
.flatMapIterable { it } // iterate over list
.flatMapSingle {
it
}
.doOnNext {
barEtries.add( BarEntry(index++, it))
}
.doOnComplete {
//We will do this when it is completed
Log.e("Result", "Completed "+barEtries)
setBarChartData()
}
.doFinally {
Log.e("Result", "Finally")
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.ignoreElements () // if you already handled everything in the doOnNext
.subscribe()
The change in getCallsCountForWeek was as below, basically just used single instead of Flowable because it made more sense and flatMapSingle provided out of the box resolution without calling even subscribe.
fun getCallsCountForWeek(calendar: Calendar) : ArrayList<Single<Float>> {
val result = ArrayList<Single<Float>>()
for(index in 0..6) {
calendar.add(Calendar.DAY_OF_MONTH, -index)
result.add(dashbordDao.getCallsCountForDay(customSharedPreferences.getUser()?.id!!, CustomDateTimeUtil.getStartOfDay(calendar), CustomDateTimeUtil.getEndOfDay(calendar)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()))
}
return result
}

Get top 10 items RxJava

I am using RxJava to get list of Posts from JSONplaceholder api.
https://jsonplaceholder.typicode.com/posts
I want to take only the top 10 from the list and save in the data base.
I am aware I need to use take operator but cannot figure out how to use that with concatMap.
Here is what I have already.
private fun loadPosts(){
subscription = Observable.fromCallable { postDao.all }
.concatMap { dbPostList ->
postApi.getPosts().concatMap { apiPostList ->
//HERE i ONLY WANT TO TAKE 10 ITEMS AND SAVE IT (HOW CAN I USE TAKE OPERATOR HERE)
postDao.insertAll(*apiPostList.toTypedArray())
Observable.just(apiPostList)
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { onRetrievePostListStart() }
.doOnTerminate { onRetrievePostListFinish() }
.subscribe(
{ result -> onRetrievePostListSuccess(result) },
{ onRetrievePostListError() }
)
}
Below code I tried and it does not work as expected.
postApi.getPosts()
.take(10) // DOES NOT WORK
.concatMap { apiPostList ->
postDao.insertAll(*apiPostList.toTypedArray())
Observable.just(apiPostList)
}
getPosts() returns a list. To use take(10) in your case you'd have to emit each element of the list individual. However, since you emit the entire list in one go, it is as if take(10) is trying to take 10 lists of posts rather than 10 posts.
I can think of 2 ways to fix this. You can convert the list to and observable like:
postApi.getPosts()
.flatMap { Observable.fromIterable(it) }
.take(10)
.toList()
Emit each item of the list, take 10 of them and collect the results in a list ready for your concatMap.
Another option is to manually slice the list:
postApi.getPosts()
.map { it.slice(0 until 10) }
Not so rx-ish but still should work.
Careful because both approaches assume there are at least 10 items in the list.

Apply Service call for every element in the list, and return only one list with RXJava

I have a method that does a call to Firebase. This method accepts a date and returns an observable.
Then I have an array of dates, that will be used as parameter of this firebase call.
I need to call the method once per item in the array, and finally concatenate to a one list.
But I don't know how to achieve it.
I'm trying to do something like:
for (dateToRetrieve in listOfDatesToRetrieve) {
val subscription = FireBaseUtils.getEventsForMap(dateToRetrieve)
.subscribeOn(Schedulers.io())
.subscribe { retrievedEventsForMap ->
val eventsList: MutableList<Event> = retrievedEventsForMap
eventListWithNoDuplicatesTotal.addAll(eventsList)
var eventListWithNoDuplicates = eventListWithNoDuplicatesTotal.distinctBy { it -> it.eventID }
this.presenter.onEventsRetrieved(eventListWithNoDuplicates as MutableList<Event>)
}
this.presenter.addSubscription(subscription)
}
But I know that is not the best solution because I'm sending the calls one by one, and adding to the list.
Is there any possibility to do it and return 1 result with the combination of all the calls?
Thanks
Try this:
val subscription = Observable.fromIterable(listOfDatesToRetrieve)
.flatMap(dateToRetrieve -> FireBaseUtils.getEventsForMap(dateToRetrieve))
.flatMapIterable { item -> item }
.toList()
.distinct { it -> it.eventID }
.subscribeOn(Schedulers.io())
.subscribe { list ->
this.presenter.onEventsRetrieved(list as MutableList<Event>)
}
this.presenter.addSubscription(subscription)

Android multithreading data

How take process multithreaded load for the item list. I get from api list string elements. Next need to get data for items this list. Load need to use rxjava. Result need do getting to the single subscribe.
Next need to get data for items this list
What it has to be? Just method inside your class or separate network request for each of strings?
For first case:
getListFromApi()
.toFlowable()
.flatMap { Flowable.fromIterable(it) }
.map { getSomeData(it) }
.toList()
.subscribe()
Second case:
getListFromApi()
.toFlowable()
.flatMap { Flowable.fromIterable(it) }
.map { requestForSomeData(it) }
.toList()
.flatMap { flowablesList -> Single.zip(flowablesList.map { it.firstOrError() }) { it.toList() } }
.subscribe()

Categories

Resources