Given the following input:
Observable<Class1> obs = {{super complex long method}}
List<Class2> inputList = {{simple list}}
I'd like to be able to create the following:
Observable<Class3> output
that emits the result of applying method input.transform(Class1 c) to each of the inputs in the inputList.
What I've come up with so far is a combination of zip & repeat:
Observable<Class3> output = Observable.zip(obs.repeat(), Observable.from(inputList),
(class1, class2) -> class2.transform(class1));
However, the repeat is way too extreme, it emits multiple repeated items before the zip kicks in.
Another thing I tried was using combineLatest, but since my List emits first, I ended up with only the last item of the list being combined with the class1 instance.
What other combination of operators might make sense?
You can just change the parameter order, like zip(Observable.from(inputList), obs.repeat(), ...).
zip will subscribe the first Observable and then the second Observable. In your example, the first Observable is infinite (e.g., obs.repeat()), RxJava will request 128 items at first. That's why you saw obs.repeat() emitted a lot of items before subscribing the second Observable.
If changing the parameter order to Observable.from(inputList), Observable.from(inputList), RxJava will subscribe Observable.from(inputList) at first, since it's a synchronous Observable, RxJava will consume it and know its length at once (assume its length is less than 128), then RxJava will request items from the second obs.repeat() using this length. So it won't require more than the necessary items.
It sounds like what you want to do is to take each input from obs, apply a set number of functions defined in a list to each of the items in obs, then flatten that output back into an Observable of type Class3. In that case I think flatMap is a good choice, because it clearly signals the intent: that you are applying many functions per item of input, then flattening the stream.
Here's an example (pardon the Java6-ness of it):
Observable<Class3> output = obs.flatMap(new Func1<Class1, Observable<Class3>>() {
#Override
public Observable<Class3> call(final Class1 class1) {
return Observable.from(inputList).map(new Func1<Class2, Class3>() {
#Override
public Class3 call(Class2 class2) {
return class2.transform(class1);
}
});
}
});
Related
Let's say I have a flowable, that some view is subscribed to and it's listening to the changes. I would like to add a custom method based on only the first emit of the flowable, but also keeping the other methods that listen to the changes. What is the best way to approach it?
The naive approach I have is to duplicate the flowable and convert it to Single or Completable to get the results, but it seems redundant.
Thank you.
Use .take(1). BTW also make sure that flowable is shared (otherwise some observers will miss events).
I think you can use share operator for that. Share operator makes a Connectable Observable. And then Connectable Observable publishes items each subscribes.
val o = Flowable.fromArray(1, 2, 3, 4, 5)
.map {
println("heavy operation")
it + it
}
.share() // publish the changes
.subscribeOn(Schedulers.computation()) // for testing. change what you want
o.take(1).subscribe { println("Special work: $it") } // take one
o.subscribe { println("Normal work: $it") }
Result
heavy operation
Special work: 2
Normal work: 2
heavy operation
Normal work: 4
heavy operation
Normal work: 6
heavy operation
Normal work: 8
heavy operation
Normal work: 10
My observable looks like following:
obs
.doOnNext { logger("Items changed (${it.size})") }
.distinctUntilChanged()
.doOnNext { logger("Items changed (${it.size})- EMIITTED") }
Log looks like following:
Items changed (7)
Items changed (7)- EMIITTED
Items changed (8)
// => missing EMIITTED message although it.size has changed => WHY?
Using the default comparator with a list of comparable items seems to fail here. Why? If the observables emitted list item size changed, the data is different, so distinctUntilChanged should not filter out the new list. But it seems like this happens here. Why?
Do I really need to provide my own comparator for distinctUntilChanged if I emit a list of items that compares the list size and the items one by one?
Edit
My obs basically looks like following:
obs = Observable.combineLatest(
RxDBDataManager.appsManager.observeList(),
RxDBDataManager.widgetsManager.observeList(),
RxDBDataManager.shortcutsManager.observeList(),
RxDBDataManager.customItemsManager.observeList(),
RxDBDataManager.foldersManager.observeList(),
Function5<List<IDBApp>, List<IDBWidget>, List<IDBShortcut>, List<IDBCustomItem>, List<IDBFolder>, List<IFolderOrSidebarItem>> { t1, t2, t3, t4, t5 ->
val list = ArrayList<IFolderOrSidebarItem>()
list.addAll(t1)
list.addAll(t2)
list.addAll(t3)
list.addAll(t4)
list.addAll(t5)
list
}
.flatMapSingle {
Observable.fromIterable(it)
.filter { it.parentType == parentType && it.parentId == parentId }
.cast(T::class.java)
.toList()
}
.flatMapSingle {
Observable.fromIterable(it)
.sorted(comparator)
.toList()
}
Additionally I apply some sorting and filtering on this data with
Based on the exchange in the comments:
RxJava users are encouraged to use immutable data types in its flows which prevents concurrency issues such as modifying the same object at different stages from different threads resulting in broken operator behavior and seemingly impossible business logic failures.
In this case, distinctUntilChanged didn't work as expected because mutable items were changed in a way that two subsequent onNext signals basically had the same content and the operator filtered them out as being non-distinct.
A way to detect if the items involved are in fact the same unintentionally is to use the bi-predicate version of the operator and then placing a breakpoint in the custom lambda. This lets one inspect the previous and current values and see if they are truly equal even if they shouldn't be:
source.distinctUntilChanged((prev, curr) -> {
// breakpoint the next line
return prev.equals(curr);
});
As in this case, broken behavior was due to a mutable item changed somewhere and thus evaluating as the same as the current/previous. With Lists, it is often not practical to breakpoint all mutation methods (such as add, addAll, set, remove etc.) but one can turn a mutable list into an immutable one and send it along the sequence. The built-in way is to convert it via the Collections::unmodifiableList:
source
.toList()
.map(Collections::unmodifiableList)
;
This will crash whenever a mutation is attempted on the now unmodifiable list instance, pointing to the logic that should be investigated further.
I have a case when I have multiple observables, each observable has its own implementation, they may be with the same type, or different I didn't decide know yet, but let's assume it they're the same type.
Observable<String> source1;
Observable<String> source2;
Observable<String> source3;
Observable<String> source4;
what I need to do now is to execute only one of them, so stream only move to the next observable if the previous one failed.
Some potential solutions:
the onErrorResumeNext() which it may be good if they're only two
observables, but in my case here, if I need to change the order of execution it will hard to update each observable.
there is the combineLatest but I don't know if it behaves the way I
described, or what modification to make work as I need.
how to achieve something like this and if they're with different types, what I need to do?
I don't know if there is better way to do it, but I would just use onErrorResumeNext() with the help of some methods for making it flexible:
Observable<String> buildObservable(Observable<String> obs, Observable<String>... subsequentObservables) {
Observable<String> observable = obs;
for (int i = 0; i < subsequentObservables.length; i++) {
observable = concatErrorObservable(observable, subsequentObservables[i]);
}
return observable;
}
where concatErrorObservable is:
Observable<String> concatErrorObservable(Observable<String> observable, Observable<String> observable2) {
return observable.onErrorResumeNext(observable2);
}
So you just need to provide the list of Observable to the buildObservable method. For example:
buildObservable(Observable.error(new Throwable("error!!")),
Observable.just("observable2"),
Observable.just("observable3"))
.subscribe(s -> Log.d(TAG, "result: " + s));
will print observable2 (in the logcat) because the first observable throws an error.
About the different types, you probably need a different map for each Observable, because I think your consumer (observer) will just expect one type of emitted data.
You can get a combined observable using onErrorResumeNext and reduce like this:
Observable<String> buildObservable(List<Observable<String>> observables) {
return Observable.fromIterable(observables)
.reduce(Observable::onErrorResumeNext)
.flatMapObservable(obs -> obs);
}
UPDATE:
To explain further, if you call the method with a list [o1, o2, o3], then
the fromIterable will return a higher-level observable equivalent to just(o1, o2, o3)
the reduce will combine the elements of this observable, sequentially calling onErrorResumeNext() with each element, like this:
o1 -> o1.onErrorResumeNext(o2) -> o1.onErrorResumeNext(o2).onErrorResumeNext(o3),
resulting in a still "higher level" 1-element observable that is equivalent to just(o1.onErrorResumeNext(o2).onErrorResumeNext(o3)).
the flatMapObservable() line will replace this 1-element observable with its one and only element itself, which is o1.onErrorResumeNext(o2).onErrorResumeNext(o3) (without the just()).
This result implements the fallback mechanism you need.
So i have a scenario in which i want to combine the newest results of two flowables and do something with it.
Flowable.combineLatest(
info,
list,
BiFunction { ... }
)
In certain conditions, i need to be able to get the the results again, and do some different stuff from before. So i could manually store the results of combinelatest somewhere, and then just reuse them but i was thinking, maybe there is a way to add a third flowable, and trigger onNext manually so the results are propagated again. Is this possible?
There are two approaches to keeping the computed value around for later use. You can create a BehaviorSubject that acts as an intermediate variable, that when defined will have the computed value, or you can publish() the observable so that newer subscribers will get the most recent results.
BehaviorSubject intermediateResult = BehaviorSubject.create();
Flowable.combineLatest(info, list, ...)
.subscribe( intermediateResult );
Alternatively,
Observable<Type> intermediateResult = Flowable.combineLatest(info, list, ...)
.replay(1)
.publish();
In either case, subscribing to intermediateResult will get the most recent computed value, if it is present.
Edit: make the function selectable on the fly:
Observable<FunctionSelector> fnSelector;
Observable<Type> intermediateResult =
Flowable.combineLatest(info, list, fnSelector,
(information, listToUse, selector) ->
getFunction(selector).apply(information, listToUse))
.replay(1)
.publish(1);
I'm coding an Android App, using RxJava, Retrofit and the Clean Architecture. The thing is that I have an Interactor/UseCase that needs to return the collection of favorite teams and favorite players. The collection of favorite teams is the results from one endpoint mixed with another. Same thing for the favorite players. In the Interactor/UseCase implementation I'm doing something like this:
getTeams = Observable.merge(mNetworkRetrofitService.getAllTeams(), mNetworkRetrofitService.getAllFavoriteTeams());
getPlayers = Observable.merge(mNetworkRetrofitService.getAllPlayers(), mNetworkRetrofitService.getAllFavoritePlayers());
Then I'm zipping this two responses together returning them in a single wrapper object:
Observable.zip(getTeams, getPlayers, new Func2<List<Team>, List<Player>, Pair<List<Team>, List<Player>>>() {
#Override
public Pair<List<Team>, List<Player>> call(List<Team> teamList, List<Player> players) {
return new Pair<>(teamList, players);
}
}).subscribe(new DefaultSubscriber<Pair<List<Team>, List<Player>>>() {
#Override
public void onNext(Pair<List<Team>, List<Player>> pair) {
callback.showResultOnUI(pair);
}
});
I know that the .zip() method is supposed to wait until the two rx.Observables end emitting the items, but in this case the mixing method is being executed more than one time.
My question is; Is there a way to keep the zip's merging method from being executed more than once?
Keep in mind this things:
I simplified the example. I'm actually also merging a cache rx.Observable with the Retrofit's rx.Observables.
The endpoints can't change. The wrapper object can't change. The response must be returned to te UI in a sigle object wrapper. We can't send the favorite teams and the favorite players in two different callback methods.
So, your zip method gets called multiple times because you act on List<T>s. This means that this Observable:
getTeams = Observable.merge(
mNetworkRetrofitService.getAllTeams(),
mNetworkRetrofitService.getAllFavoriteTeams());
Will have 2 items, each one a list - it won't merge them all together. What you want is to merge both lists into one:
getTeams = Observable.merge(
mNetworkRetrofitService.getAllTeams(),
mNetworkRetrofitService.getAllFavoriteTeams())
.flatMap(Observable::fromIterable()
.toList();
If you do this for both of your zip parameters, then you will get only one emission.