RxJava - fetch every item on the list
Asked Answered
P

5

46

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?

Parasynapsis answered 19/1, 2015 at 23:8 Comment(2)
Is ordering important?Ultramundane
Not really, but for educational purposes is really like to see both versionsParasynapsis
D
59

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>
Dorthydortmund answered 19/1, 2015 at 23:42 Comment(1)
You could also do Observable.from(Arrays.<Integer>asList(...)) and not need the flatMapIterableUltramundane
M
11

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)
Momentum answered 7/6, 2017 at 16:17 Comment(1)
Please take a look at this question: #48959854Splenectomy
A
4

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.

Abijah answered 7/4, 2015 at 18:44 Comment(0)
D
4

In Kotlin use flattenAsFlowable:

repository.getFlowableData(id)
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.computation())
    .toList()
    .flattenAsFlowable { it }
    .map { someMethod(it) }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ },
        { onError(it) })
Disgust answered 13/6, 2018 at 12:8 Comment(0)
C
2

For Kotlin

 Observable
        .fromIterable(listOfChallenges) // list of challenges which contain challenge ID
        .flatMap { eachChallenge ->
            getChallengeDetail(eachChallenge.challengeUid!!) // fetch details of each challenge
        }
        .toList() // map to list
        .subscribe({ listOfEachChallengesDetail ->

        }, {
            // error
        })
Canescent answered 25/10, 2020 at 9:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.