Get top 10 items RxJava - android

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.

Related

Fetching a Single<List> for all elements of another Single<List> in RxKotlin

A, B, C are objects
All function calls are made to a Rooms DB
This code snippet is inside a ViewModel
repo = Repository
So I'm making an android app (can't provide details) and for a particular screen I need to do the following.
My first call is repo.getInfo, which returns a Single Observable ListOfA: Single<List<A>> //perform some operations
for every element of ListOfA I need to call another function repo.getB(A) which returns a Single Observable ListOfB: Single<List<B>> //perform some operations
for every element of ListOfB I need to call another function repo.getC(B) which returns a Single Observable ListOfC: Single<List<C>> //perform some operations
after I have the required data I need to call another function that combines the data to display on the UI.
Now I can't get this to work. Here's what I've tried. But the flow stops at the line marked THIS LINE and jumps to subscribe block.
Individual calls to the functions work so the data is not the problem.
I'm pretty new at this and quite frankly out of my depth. Any help or hint is appreciated. Thanks
localListOfA = emptyList<A>()
localListOfB = emptyList<B>()
localListOfC = emptyList<C>()
compositeDisposable.add(
getInfo.map{listOfA ->
localListOfA.addAll(listofA)
listOfA.map {elementA -> ////THIS LINE
getB(elementA.id).map{listOfB ->
listOfB.filter {
//some logic to select a few objects
}
}.map { it // filtered list of B
localListofB.addAll(it)
localListOfB.last() //I only need the top element of this list
}.map{elementB ->
getC(elementB.id).map{ listOfC ->
localListOfC.addAll(listOfC)
//do some operations
}
}
}
}
.subscribeOn(DEFAULT_CACHED_SCHEDULERS)
.observeOn(AndroidSchedulers.mainThread())
.doOnError(/*take log*/)
.subscribe{
prepareUi()
}
)
You can flatten a List into an Observable using .flattenAsObservable
getInfo // Single<List<A>>
.doOnSuccess { localListOfA.addAll(it) } // Side effect, adding to localListOfA
.flattenAsObservable { it } // Observable<A>
.flatMapSingle { elementA -> getB(elementA.id) } // Observable<List<B>>
.map { it.filter { true } } // Some logic to select a few objects from B
.doOnNext { localListOfB.addAll(it) } // Side effect, adding to localListOfB
.map { it.last() } // Observable<B>, only the last element
.flatMapSingle { elementB -> getC(elementB.id) } // Observable<List<C>>
.doOnNext { localListOfC.addAll(it) } // Side effect, adding to localListOfC
.flatMapIterable { it } // Observable<C>
Now, you mentioned you need to combine this data somehow. In Rx you can nest chains in order to access the intermediate data. For example, if you have a call that returns a Single<Foo> and you need Foo for the function getBar(foo: Foo): Single<Bar>, one way of achieving this is as follows:
getFoo().flatMap { foo -> // .concatMap, .switchMap
getBar(foo).map { bar ->
// Use both foo and bar
}
}

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)

RxJava - Mapping a result of list to another list

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) }
}

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()

Change Flowable<List<Obj1>> to Flowable<List<Obj2>> in room

How can I read a flowable list of values from room and convert it to another object which is a combination of more values from room
database.leadsDao().getLeads(leadState.name)
.flatMap {
val len = it.size.toLong()
Flowable.fromIterable(it)
.flatMap {
Flowable.zip(
database.orderDao().getById(it.orderId),
database.orderMedicineDao().getByOrderId(it.orderId),
database.patientDao().getById(it.patientId),
Function3<Order, List<OrderMedicine>, Patient, LeadDetail>
{ order, orderMedicines, patient -> LeadDetail.from(it, patient, order, orderMedicines) })
}
.take(len)
.toList()
.toFlowable()
}
The code above works but I don't like the take(len) part. And without it, the stream never calls onNext of the subscriber. The stream keeps waiting for more items, which shouldn't happen since Flowable.fromIterable gives finite number or items and then ends. I.e., the code below doesn't work
database.leadsDao().getLeads(leadState.name)
.flatMap {
Flowable.fromIterable(it)
.flatMap {
Flowable.zip(
database.orderDao().getById(it.orderId),
database.orderMedicineDao().getByOrderId(it.orderId),
database.patientDao().getById(it.patientId),
Function3<Order, List<OrderMedicine>, Patient, LeadDetail>
{ order, orderMedicines, patient -> LeadDetail.from(it, patient, order, orderMedicines) })
}
.toList()
.toFlowable()
}
Flowable.fromIterable gives finite number or items and then ends.
But the Flowable.zip inside of the flatmap will not end, since Room's DAO objects emit the current value AND all future updates, so the database.*() calls that are zipped together are not finite. If you add a .first() call to the inner Flowable.zip the second version should work as well.

Categories

Resources