Filter RxJava/ReactiveX Observable based on Observable condition - rx-java2

I need to filter a Stream of observable Values based on some condition which is modeled as Observable inside my Value.
class ValueObject {
BehaviorSubject<Boolean> condition = ...;
}
...
valueObjects.filter(valueObject -> condition).subscribe(valueObject -> ...);
My goal is to have the stream firing if a) a new Value was pushed or b) the condition for the filter changed.
(Bonus: do that same thing with lists of ValueObjects (Observable<List<ValueObject>>) and that ugly Object[]-combineLatest...)
Is there some kind of best practice for that problem? Thanks.

Related

How to manipulate object coming from a Flux<Object> with a value coming from a method emitting Mono<Items> in non-blocking way?

I am trying to manipulate my objects received from Flux with data received from a Mono where the methods emitting the Flux of object and Mono of items are both different API calls. The problem is, I don't have control over the threads and the items received from the Mono are never assigned to my object unless I intentionally block() that thread. Kindly suggest if any non-blocking way possible for this scenario.
I have also looked into Schedulers, subscribeOn, publishOn but unable to figure out the pipeline.
public Flux<Object> test {
method1().map(obj -> {
if (obj.getTotalItems() > 20) {
obj.setItems(method2(obj).block());
}
return obj;
});
}
Here method1 is emitting Flux of objects received from API hit.
And method2 is emitting a list of items fetched from another API hit.
How can I make this whole flow non-blocking?
Try flatMap or concatMap
using flatMap operator you can flatten substream in non-blocking public
Flux<Object> test {
method1().flatMap(obj -> {
if (obj.getTotalItems() > 20) {
return method2(obj)
.map(result -> {
obj.setItems(result);
return obj;
});
}
return Mono.just(obj);
});
}
flatMap allows you to flatten several streams at a time, so in case of long-running operations, you may in more efficient process elements.
One downside of flatMap is that it does not preserve the order of elements so if you have a sequence of upstream elements like [1, 2, 3, 4] with flatMap there is a chance that the order will be changed because of asynchronous nature of substreams.
To preserve order, you can use concatMap which flatten only once streams at a time, so there are guarantees that order of flattening elements will be preserved:
Flux<Object> test {
method1().concatMap(obj -> {
if (obj.getTotalItems() > 20) {
return method2(obj)
.map(result -> {
obj.setItems(result);
return obj;
});
}
return Mono.just(obj);
});
}
Note
Mutation of the objects such a way is not the best idea, and I would prefer to use immutable object pattern object in reactive programming

Multicast sticky observable with on subscribe/on dispose behavior

I'm trying to create an Observable with the following characteristics:
allows multiple concurrent and/or consecutive subscribers
emits the last emitted item to every new subscriber
does something when the first subscriber subscribes, and when the last subscription is disposed
A BehaviorSubject with doOnSubscribe/doOnDispose satisfies #1 and #2, but runs subscribe/dispose for every subscriber instead of only the first and last. Adding share satisfies #1 and #3, but only emits the last emitted item to the first concurrent subscriber.
I came up with a solution that seems to work but feels like an ugly hack:
AtomicInteger subs = new AtomicInteger();
Observable<String> test = BehaviorSubject.createDefault("foo")
.doOnSubscribe(x -> {
if(subs.getAndIncrement() == 0) {
// do something
}
})
.doOnDispose(() -> {
if(subs.decrementAndGet() == 0) {
// do something
}
});
Is there an existing operator or combination of operators that achieves the same effect?
Use the replay operator with argument 1 i.e.
yourObservable.replay(1)
Edit: You are right that replay will return a connectedObservable and that the refcount operator will make it behave like on Observable i.e.
yourObservable.replay(1).refcount()

Array of observables where the output of each is the input of the next

I'm trying to use RxSwift to execute actions on multiple data sources. However, I have no idea how to accomplish the following.
I have an array of observabless where the output of each, should be the input of the next. So, I want to do something like, get the first observable, wait for the result and pass it to the next, all the way to the end of the array and return one final value.
Is that possible? Thanks in advance.
*** Update: Ok, I'll be more specific as requested.
The 'observables' I'm using in the array, are custom. I use a function that returns Observable.create { ... }. Inside the closure, I run an asynchronous operation that transforms the value and then send the result to the observer before completing. That resulting value, must pass to the next observable, and so on to the last observable in the array to get a final value.
The observables may send multiple values, but they must pass from one observable to the next like an assembly line.
It is difficult to know exactly what you are asking for, since Observables do not exactly have inputs but I think this is a common problem.
You may be looking for a combination of the concat or reduce operators, which allow you to accumulate data from the values emitted from an Observable. See ReactiveX's documentation for Mathematical and Aggregate Operators.
Hopefully this can get you started:
// "I have an array of observables..."
let one = Observable.deferred { Observable.just(1) }
let two = Observable.deferred { Observable.just(2) }
let observables = [one, two]
// "the output of each, should be the input of the next"
// this is problematic, because observables do not strictly have inputs.
let resultsFromEach = Observable.concat(observables)
resultsFromEach
.reduce(0) { result, next in
result + 1
}
.debug("result")
.subscribe()

can i conditionally "merge" a Single with an Observable?

i'm a RxJava newcomer, and i'm having some trouble wrapping my head around how to do the following.
i'm using Retrofit to invoke a network request that returns me a Single<Foo>, which is the type i ultimately want to consume via my Subscriber instance (call it SingleFooSubscriber)
Foo has an internal property items typed as List<String>.
if Foo.items is not empty, i would like to invoke separate, concurrent network requests for each of its values. (the actual results of these requests are inconsequential for SingleFooSubscriber as the results will be cached externally).
SingleFooSubscriber.onComplete() should be invoked only when Foo and all Foo.items have been fetched.
fetchFooCall
.subscribeOn(Schedulers.io())
// Approach #1...
// the idea here would be to "merge" the results of both streams into a single
// reactive type, but i'm not sure how this would work given that the item emissions
// could be far greater than one. using zip here i don't think it would every
// complete.
.flatMap { foo ->
if(foo.items.isNotEmpty()) {
Observable.zip(
Observable.fromIterable(foo.items),
Observable.just(foo),
{ source1, source2 ->
// hmmmm...
}
).toSingle()
} else {
Single.just(foo)
}
}
// ...or Approach #2...
// i think this would result in the streams for Foo and items being handled sequentially,
// which is not really ideal because
// 1) i think it would entail nested streams (i get the feeling i should be using flatMap
// instead)
// 2) and i'm not sure SingleFooSubscriber.onComplete() would depend on the completion of
// the stream for items
.doOnSuccess { data ->
if(data.items.isNotEmpty()) {
// hmmmm...
}
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ data -> /* onSuccess() */ },
{ error -> /* onError() */ }
)
any thoughts on how to approach this would be greatly appreciated!
bonus points: in trying to come up with a solution to this, i've begun to question the decision to use the Single reactive type vs the Observable reactive type. most (all, except this one Foo.items case?) of my streams actually revolve around consuming a single instance of something, so i leaned toward Single to represent my streams as i thought it would add some semantic clarity around the code. anybody have any general guidance around when to use one vs the other?
You need to nest flatMaps and then convert back to Single:
retrofit.getMainObject()
.flatMap(v ->
Flowable.fromIterable(v.items)
.flatMap(w ->
retrofit.getItem(w.id).doOnNext(x -> w.property = x)
)
.ignoreElements()
.toSingle(v)
)

Aggregate PropertyChanged events from the collection into one IObservable<EventPattern<PropertyChangedEventArgs>>

I have collection of INotifyPropertyChanged objects and would like to stream all PropertyChanged events into a single observable sequence for further processing.
Here is a code
IObservable<EventPattern<PropertyChangedEventArgs>> _allEvents = null;
// Items contains collection of item, which implements INotifyPropertyChanged
foreach (var item in Items)
{
var seq = Observable.FromEventPattern<PropertyChangedEventArgs>(item, "PropertyChanged");
if (_allEvents == null)
_allEvents = seq;
else
_allEvents.Merge(seq);
}
// subscribe to the aggregated observable sequence
if (_allEvents != null)
_allEvents.Subscribe(evt => Trace.WriteLine(" Property Changed -> " + evt.EventArgs.PropertyName));
A single Subscribe doesn't work here for some reason in the aggregated sequence.
Looks like I aggregated (using Reactive Extensions's Merge function) incorrectly. But, subscribe inside the loop works perfectly.
Can anybody assist me here, how to aggregate a many event streams into one with reactive extensions?
Thanks
Try this:
var _allEvents = Observable
.Merge(Items
.Select(item => Observable
.FromEventPattern<PropertyChangedEventArgs>(item, "PropertyChanged")));
_allEvents.Subscribe(evt => Trace.WriteLine(" Property Changed -> " + evt.EventArgs.PropertyName));
The reason your approach doesn't work is that you were calling IObservable<T> Observable.Merge<T>(this IObservable<T> first, IObservable<T> second). The return type of this is a resulting observable. I think you might have been thinking that Merge modified the Observable, but you can think of Observables as immutable (sort of). The way to get your approach to work would have been:
_allEvents = _allEvents.Merge(seq);
But... yuck. Don't do that.