Tibo
Tibo

Reputation: 533

RXJava handling nested calls

Imagine I have a service to retrieve some authors (id, firstname, lastname, birthday, nationality) and a second service to retrieve an author's book (title, summary, number of pages). I would like my network call to return me a list of authors with their books.

To illustrate, my services would return me AuthorOResponse and BookResponse and I would like to emit an Observable of AuthorObject.

public class AuthorResponse {
    private String id;
    private String firstname;
    private String lastname;
    private Date birthday;
    private String nationality;
}

public class BookResponse {
    private String title;
    private String summary;
    private int pageNumber;
}

public class AuthorObject {
    private String id;
    private String firstname;
    private String lastname;
    private Date birthday;
    private String nationality;
    private List<BookObject> books
}

My services would be something like

Observable<List<AuthorResponse>> getAuthors()
Observable<List<BookResponse>> getAuthorBooks(String authorId)

I would like to use them to emit an Observable but cannot figure out how to do it since each of my calls to getAuthorBooks need an author.

I came up with something looking like this but I have an issue since my concatMap make me "loose" my author.

myServices.getAuthors()
                .map(author -> createAuthor())
                .concatMap(author -> mWebService.getAuthorBooks(author.getId())
                .flatMapIterable(data -> data.items)
                .map(book -> addBook())

Does someone know how I could do this ?

Upvotes: 1

Views: 2643

Answers (2)

Maksim Ostrovidov
Maksim Ostrovidov

Reputation: 11058

You could use special flatMap overload designed for such cases:

myServices.getAuthors()
    .map(author -> createAuthor())
    .flatMap(author -> mWebService.getAuthorBooks(author.getId()),
            (author, data) -> {
                author.setBooks(data.items));
                return Observable.just(author);
            })
    .toList() //here is your Observable<List<Author>>

Upvotes: 6

yosriz
yosriz

Reputation: 10267

Maybe something like this:

getAuthors.map(new Func1<List<AuthorResponse>, AuthorObject>() {
        @Override
        public AuthorObject call(List<AuthorResponse> authorResponses) {
            return createAuthor();
        }
    }).flatMap(new Func1<AuthorObject, Observable<AuthorObject>>() {
        @Override
        public Observable<AuthorObject> call(final AuthorObject authorObject) {
            return getAuthorBooks(authorObject.id)
                    .map(new Func1<List<BookResponse>, AuthorObject>() {
                        @Override
                        public AuthorObject call(List<BookResponse> bookResponses) {
                            authorObject.books = parseBooks(bookResponses);
                            return authorObject;
                        }
                    });
        }
    });

Query the Authors, map each AuthorResponse to AuthorObject, then using flatMap query books for each Author, and map it in order to add parsed books to the generated earlier AuthorObject. at the end you'll get an Observable that emits AuthorObject with their books list populated.

Upvotes: 2

Related Questions