RxJava2. Execute a request for every item in a list
Asked Answered
P

3

6

I have a list List<FileModel>

FileModel is just a class contains id: Int

id - is ID of photo file I need to fetch and cast to Bitmap

I have a request:

fun getFile(fileId: Int): Single<ResponseBody>

This request returns ResponseBody which we can cast to Bitmap

And

fun generatePhoto(responseBody: ResponseBody): Single<Bitmap?>

What I want is to create a function

fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
    // Execute getFile(...) for each item in the list
    // Cast getFile(...) result to Bitmap using generatePhoto(...)
    // Return a list of Bitmaps
}

I've tried something like this but it's completely wrong

fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
    return Observable.fromIterable(list)
        .flatMap { getFile(it.id) }
        // How to call generatePhoto(...) here?
}
Pharmacist answered 11/5, 2018 at 13:36 Comment(2)
I'm not an expert in Rx, could you provide an example?Pharmacist
Actually, it's probably just supposed to be flatMap.Amorphous
U
7

You can do it like this:

fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
    // Get an Observable of the list
    return Observable.fromIterable(list)
        // Get a Single<ResponseBody> for every FileModel
        .flatMapSingle { getFile(it.id) }
        // Get a Single<Bitmap> for every ResponseBody
        .flatMapSingle { file -> generatePhoto(file) }
        // Put everything back on a list
        .toList()
}

This way you can iterate over the list flapMapping for your needs and then putting it back together as a list in the end. The toList() operator is just a convenience that puts together the items emitted previously.

And to call this function just go:

    getPhotos(list)
        .doOnSuccess { resultList ->
          Log.d("Rx", "doOnSuccess.resultList=[$resultList]")
        }
        .subscribe()

By the way, if you have RxKotlin as a dependency you can get an Observable from a List with an extension function, like this: myList.toObservable()

Unsuspected answered 11/5, 2018 at 14:35 Comment(0)
T
1

Observable.fromIterable(list) should emit an event for each item in the list which means that you're return type in getPhotos be Flowable<Bitmap> as Flowable can return more than one value.

I think what you actually want is:

fun getPhotos(list: List<FileModel>): Flowable<Bitmap> {
    return Observable.fromIterable(list)
        .map { getFile(it.id) }
        .map { generatePhoto(it) }
}

if you want to emit a list of Bitmaps you can use single like this

fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
    return Single.just(list)
        .map { it.map { getFile(it.id) } }
        .flatMap { 
            Single.just(it.map { generatePhoto(it) }) 
        }
}
Thyself answered 11/5, 2018 at 14:8 Comment(3)
But if we use Single.just(list) inside map { } we will have List<FileModel> instead of FileModelPharmacist
i've updated the answer, you can iterate over each list inside of the map blockThyself
you can flatMap to evaluate the single but you have to emit another single in that caseThyself
A
1

Have you tried:

fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
    return Observable.fromIterable(list)
        .flatMapSingle { getFile(it.id) }
        .flatMapSingle { generatePhoto(it) }
}

?

Amorphous answered 11/5, 2018 at 14:33 Comment(2)
We can't call flatMapSingle after flatMapPharmacist
You're right, maybe it should be flatMapSingle on both.Amorphous

© 2022 - 2024 — McMap. All rights reserved.