RxJava - fetch every item on the list

I have a method that returns an Observable<ArrayList<Long>>, which are ids of some Items. I'd like to go through this list and download every Item using another method that returns Observable<Item>.

How would I do this using RxJava operators?


Here's a small self contained example

public class Example {

    public static class Item {
        int id;
    }

    public static void main(String[] args) {
        getIds()
                .flatMapIterable(ids -> ids) // Converts your list of ids into an Observable which emits every item in the list
                .flatMap(Example::getItemObservable) // Calls the method which returns a new Observable<Item>
                .subscribe(item -> System.out.println("item: " + item.id));
    }

    // Simple representation of getting your ids.
    // Replace the content of this method with yours
    private static Observable<List<Integer>> getIds() {
        return Observable.just(Arrays.<Integer>asList(1, 2, 3));
    }

    // Replace the content of this method with yours
    private static Observable<Item> getItemObservable(Integer id) {
        Item item = new Item();
        item.id = id;
        return Observable.just(item);
    }
}

Please note that Observable.just(Arrays.<Integer>asList(1, 2, 3)) is a simple representation of Observable<ArrayList<Long>> from your question. You can replace it with your own Observable in your code.

This should give you the basis of what you need.

p/s : Use flatMapIterable method for this case since it belongs to Iterable as explained below:

/**
 * Implementing this interface allows an object to be the target of
 * the "for-each loop" statement. See
 * <strong>
 * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides /language/foreach.html">For-each Loop</a>
 * </strong>
 *
 * @param <T> the type of elements returned by the iterator
 *
 * @since 1.5
 * @jls 14.14.2 The enhanced for statement
  */
 public interface Iterable<T>

As an alternative to flatMapIterable you can do this with flatMap:

Observable.just(Arrays.asList(1, 2, 3)) //we create an Observable that emits a single array
            .flatMap(numberList -> Observable.fromIterable(numberList)) //map the list to an Observable that emits every item as an observable
            .flatMap(number -> downloadFoo(number)) //download smth on every number in the array
            .subscribe(...);


private ObservableSource<? extends Integer> downloadFoo(Integer number) {
   //TODO
}

Personally I think .flatMap(numberList -> Observable.fromIterable(numberList)) is easier to read and understand than .flatMapIterable(numberList -> numberList ).

The difference seems to be the order (RxJava2):

  • Observable.fromIterable: Converts an Iterable sequence into an ObservableSource that emits the items in the sequence.
  • Observable.flatMapIterable: Returns an Observable that merges each item emitted by the source ObservableSource with the values in an Iterable corresponding to that item that is generated by a selector.

Using method references this looks like:

Observable.just(Arrays.asList(1, 2, 3))
            .flatMap(Observable::fromIterable)
            .flatMap(this::downloadFoo)

Use a Transformer that modifies the source Observable, calling a flatMap on it with a function. You can think of this as a 2-step process:

  1. The function takes each emitted item (an Iterable<T>) and re-emits it as an Observable<T>
  2. flatMap takes each of these emitted Observable<T> objects an merges them into a single Observable<T>

The Transformer looks like this:

public class FlattenTransform<T> implements Observable.Transformer<Iterable<T>, T> {
    @Override
    public Observable<? extends T> call(Observable<? extends Iterable<T>> source) {
        return source.flatMap(new Func1<Iterable<T>, Observable<T>>() {
            @Override
            public Observable<T> call(Iterable<T> values) {
                return Observable.from(values);
            }
        });
    }
}

Once you've created your Transformer, you can use compose to apply the transformation on the source observable:

public class Example {

    private static final ArrayList<Long> sourceList = new ArrayList<>(Arrays.asList(new Long[] {1L,2L,3L}));
    private static final Observable<ArrayList<Long>> listObservable = Observable.just(sourceList);
    private static final FlattenTransform<Long> flattenList = new FlattenTransform<Long>();

    public static void main(String[] args) {
        listObservable.compose(flattenList).subscribe(printItem);
    }

    private static Action1<Long> printItem = new Action1<Long>() {
        @Override
        public void call(Long item) {
            System.out.println("item: " + item);
        }
    };
}

The advantage of using a compose with a Transformer instead of a flatMap with a Func1 is that if in the future if you need to flatten a list again, you won't even have to think about which operator to use (map? flatMap? concatMap?). In other words, the flatmap operation is baked into the FlattenTransform class and that detail is abstracted away.

Transformers also have other benefits, such as being able to chain together multiple operations.