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?
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):
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:
Iterable<T>
) and re-emits it as an Observable<T>
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.
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) })
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
})
© 2022 - 2024 — McMap. All rights reserved.