Filter RxJava Observable with other Observable - java

I'm using RxAndroid 2.0.1 with RxJava 2.0.6.
I have two observables: one returns Maybe<MyObject> based on some String (ID). When the optional object is returned, I have to call the second one that takes the MyObject instance and returns Single<Boolean> if object meets some conditions. Then I can do some further operations with the object instance.
My current implementation is as follows:
objectDAO.getById(objectId)
.subscribe(
myObject -> checkCondition(myObject),
throwable -> /* Fallback */,
() -> /* Fallback */
);
private void checkCondition(final MyObject myObject) {
otherDAO.checkCondition(myObject)
.subscribe(
isTrue -> {
if (isTrue) {
// yay! now I can do what I need with myObject instance
} else {
/* Fallback */
}
},
throwable -> /* Fallback */
);
}
Now I'm wondering how could I simplify my code. My ideas:
Try to use zip - I can't because second Observable can't be subscribed until the first one returns the MyObject
Try to use filter - Now the issue is that I need to use blocking get to call second observable. It will propably work, but looks like a code smell:
objectDAO.getById(objectId)
.filter(myObject ->
otherDAO.checkCondition(myObject).blockingGet()
)
.subscribe(
myObject -> checkCondition(myObject),
throwable -> /* Fallback */,
() -> /* Fallback */
);
Try to use flatMap - The second observable returns Boolean while I need to return the original object. Because of that I need to mape a code snippet with blockingGet and return original object or Maybe.empty()
Any suggestions how to do it in such a way that the code is "clean" (it's smaller and it's still clear what's happening inside)?

One thing you could do:
objectDAO.getById(objectId)
.flatMapSingle(myObject -> otherDAO
.checkCondition(myObject)
.map(isTrue -> Pair.create(myObject, isTrue))
)
Then you have an Observable<Pair<MyObject, Boolean>> and can proceed however you want: subscribe directly and check the Boolean there, filter by the Boolean value, etc.

The RxJava2Extensions extra project by akarnokd has a filterAsync transformer (to be used with compose) that does just that, using a any Publisher<Boolean> ;)

I've come up with solution without passing Pairs with boolean values, in case anybody will face same problem.
For example, if objectDAO.getById(objectId) returns Observable<T> and otherDAO.checkCondition(myObject) returns Single<Boolean>, we can handle filtering in such a way:
objectDAO.getById(objectId)
.flatMap(myObject -> otherDAO
.checkCondition(myObject)
.toObservable()
.filter(Boolean::booleanValue)
.map(o -> myObject))
.flatMapSingle(obj -> ...)
Unwanted objects will be resolved to Observable.empty and thus filtered, so that only needed objects will get to .flatMapSingle(obj -> ...)
Basically, same thing can be achieved with slightly easier to understand structure (though, I've found the first one a bit nicer aesthetically):
objectDAO.getById(objectId)
.flatMap(myObject -> otherDAO
.checkCondition(myObject)
.flatMapObservable(isChecked -> isChecked ? Observable.just(myObject) : Observable.empty()))
.flatMapSingle(obj -> ...)

Related

How to chain vavr's Either nicely?

I have a function returning an Either<MyError, String> (function2) , which result depends on another function returning another Either<MyError, SomethingElse> (function1)
Both functions rely on a Try block that could fail, and I want to compose those two first function to create a "handle" which will be the main function of my class.
There are basically 3 scenarios possible :
function1 fails : I want handle to return the error given by function1
function1 succeeds and function2 fails : function2 must return its own error then returned by handle
both functions work : handle must return the String
Here is my current code :
private Either<MyError, Path> getPath(Arg arg) { // function 1
return Try.of(() -> //some code that can fail)
.toEither().mapLeft(e -> new MyError("Error message for function1", e));
}
private Either<MyError, String> getContent(Path path) { // function 2
return Try.of(() -> //some code that can fail)
.toEither().mapLeft(e -> new MyError("Error message for function2", e));
}
public Either<MyError, String> handle(Arg arg) {
return Either.right(arg)
.map(this::getPath)
.map(this::getContent);
}
Everything works except the Handle function, I think that my problem might be related to the use of Either::map function, that might not be the thing for my case.
Any thought about this ?
Also, sorry if the answer seems obvious, i am quite new to functionnal programming and vavr.
The method that could help to make this work would be flatMap.
So if you use flatMap instead of map, the handle method will become something like:
public Either<MyError, String> handle(Arg arg) {
return Either.<MyError, Arg>right(arg)
.flatMap(this::getPath)
.flatMap(this::getContent);
}
The scenarios you mentioned are all covered with this flatMap method.
See the Either.flatMap documentation for the official docs about it.

Is it possible to create a dynamic filter/predicate in Mono/Flux?

The app is a simple processing - reading data, filtering it, process and write filtered data.
Here is a simple code which runs without any issue:
void run() {
Flux.interval(Duration.ofMillis(200))
.filter(value -> getPredicate().test(value))
.flatMap(this::writeData)
.subscribe();
}
private Predicate<Long> getPredicate() {
return value -> value % 2 == 0;
}
Is it possible to have dynamic predicate which will be retrieved from remote web service with periodic requests?
If possible - how to use Mono<Predicate> inside .filter() and keep it non-blocking
For example replacing getPredicate() with below:
private Mono<Predicate<Long>> getPredicateFromRemoteServer() {
return webClient.get()
.uri("/filters/1")
.retrieve()
.bodyToMono(Filter.class)
.map(this::mapToPredicate)
.cache(v -> Duration.ofMinutes(10), ex -> Duration.ZERO, () -> Duration.ZERO);
}
private Predicate<Long> mapToPredicate(Filter filter) {
// here will be converting filter object into predicate
return value -> value > 5;
}
Ideally I would like to avoid cache(Duration.ofMinutes(10)) because filter could be updated each minute, or each day... and once filter is updated my service get notified, but I didn't find a way to invalidate cache externally, that's why Duration.ofMinutes(10) is used for some approximate invalidation.
Well, perhaps you could write the pipeline a bit differently. Instead of aspiring to return a new Predicate every time your process an item in your stream by calling getPredicateFromRemoteServer(), you could make the function itself your predicate. Pass the value you are processing from the stream and make it return a Mono<Boolean> with the answer and use that in a filterWhen pipe in your pipeline.
For example, somewhat like this:
private Mono<Boolean> isWithinThreshold(int value) {
return webClient.get()
.uri("/filters/1")
.retrieve()
.bodyToMono(Filter.class)
.map(filter -> filter.threshold <= value)
.cache(v -> Duration.ofMinutes(10), ex -> Duration.ZERO, () -> Duration.ZERO);
}
Then in your main pipeline you can do:
Flux.interval(Duration.ofMillis(200))
.filterWhen(value -> isWithinThreshold(value))
.flatMap(this::writeData)
.subscribe();
}

RxJava2 - Merge values and side effects from a stream

I'm trying to extract some common logic, based on RxJava2, into reusable components. Let's imagine I have the following piece of code:
someSingle
.doOnSuccess { // update UI based on side effect }
.subscribeOn(...)
.observeOn(...)
.subscribe(
value -> // update UI based on value
throwable -> // handle error
)
I want to wrap this into a reusable component, exposing a method that returns a Flowable of events. The clients will receive events and update the UI accordingly. My goal is not to have any reference of the view inside the reusable component. I want the method to be something like this:
fun reusableMethod(...) : Flowable<Event> { ... }
Event is a sealed class, enclosing two sub types - SideEffectEvent and ValueEvent.
What is the best way to transform the stream from the first snippet, so I can get both the side effect and the value to be emitted as flowable values?
Currently, I have the following solution, but I'm not very happy with it, because it looks a bit clunky and complex:
private val sideEffectEvents = PublishProcessor.create<SideEffectEvent>()
fun reusableMethod(...) =
Flowable.merge(
someSingle.doOnSuccess { sideEffectEvents.onNext(SideEffectEvent()) },
sideEffectEvents
)
.subscribeOn(...)
.observeOn(...)
I have also considered some alternatives:
Notify the client for SideEffectEvents using a callback that is passed to someReusableMethod() - looks very unnatural and having a callback and a stream to subscribe to is not a good code style
Use a single PublishProcessor. Post side effects to it and use it to subscribe to the original Single. Expose a cleanUp() method in the reusable component so the client can dispose of the stream when it decides to.
I'm looking forward to suggestions and ideas.
First of all it doesn't have to be a Flowable. It can be a simple Observable. But the below solution should work in both cases. read more here Observable vs Flowable
This code is not tested, I have written it to give you a simplified idea about how you can achieve this.
// a sealed class representing current state
sealed class ViewState {
object Loading : ViewState() // using object because we do not need any data in cass of loading
data class Success(val data: List<Model>) : ViewState()
data class Error(val t: Throwable) : ViewState()
}
// an observalbe or flowable returning a single object ViewState
// it will always return ViewState class containing either data or error or loading state
return service.getData()
.map { data -> ViewState.Success(data) } // on successful data fetch
.startWith(ViewState.Loading()) // show loading on start of fetch
.onErrorReturn { exception -> ViewState.Error(exception) } // return error state
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// somewhere in Activity or in multiple activities subscribe to above observable
subscribe({ viewState ->
when {
viewState.Loading -> showProgressView()
viewState.Error -> showErrorView(viewState.t)
viewState.Success -> showData(viewState.data)
else -> IllegalArgumentException("Invalid Response")
}
})
How about this:
Before:
someSingle
.operation1()
.operation2()
.doOnSuccess { // update UI based on side effect }
.operation3()
.operation4()
.subscribeOn(...)
.observeOn(...)
.subscribe(
value -> // update UI based on value
throwable -> // handle error
)
Reusable:
fun reusableMethod(...): Flowable<Event> =
someSingle
.operation1()
.operation2()
.flatMapPublisher {
Single.concat(
Single.just(getSideEffectEvent(it)),
Single.just(it)
.operation3()
.operation4()
.map { value -> getValueEvent(value) }
)
}
.subscribeOn(...)
.observeOn(...)
You can further simplify this using Flowable#startWith, and avoiding Single#concat()

How to handle data value inside Optional type of List data

I want to simplify my code without using too many If-Else condition.
The business logic I want is:
I retrieved data of customer's account from DB
I want to check whether each customer is qualified for applying new product
If She/He has the account, then passed
Otherwise failed.
Repository
public interface MyRepository extends JpaRepository<Account, String>{
Optional<List<Account>> findAcctByCustoNo(String custNo);
}
The logic code
Optional<List<Account>> accounts = myRepo.findAcctByCustoNo(auth.getCustNo());
if(!accounts.isPresent()) {
return "invalid param";
}
accounts.ifPresent(list->list.stream()
.filter(type -> type.getAccCd().equals("typeA") || type.getAccCd().equals("typeB"))
.forEach(System.out::println));
The code hasn't finished yet. I need to check, after filtering the data, if it still return null value, I want to return "No Data" message, something like this. Or apply another method or else.
And I don't know how to do it properly. Because What I can think is add create new instance after filtering, and check it with isPresent again. But I have a feeling that I can do it inside the accounts instance.
Please enlighten me.
I just use this Optional feature recently. I spent a lot of time understanding the method inside it. I was thinking to utilize map, but again, I have no idea how to implement it in the right way.
What about this ?
Do all the filtering you want, then use findAny (cheaper than count() since it will stop as soon as it has a match) and check the result
Optional<List<Account>> accounts = myRepo.findAcctByCustoNo(auth.getCustNo());
return accounts //
.map(List::stream) //
.orElseGet(Stream::empty) //
.filter(type -> type.getAccCd().equals("typeA") || type.getAccCd().equals("typeB")) //
.findAny() //
.orElse(null)
!= null;
Explanation
map your optional List to a stream
if not, use an empty stream
then, filter all you want
findAny ? we are good.
orElse, return null
And finally, check for null.
Test code on simpler data :
#Test
public void test_checkArray() {
Assert.assertFalse(this.checkArray(null));
Assert.assertFalse(this.checkArray(Arrays.asList()));
Assert.assertFalse(this.checkArray(Arrays.asList("not a!", "b", "c")));
Assert.assertTrue(this.checkArray(Arrays.asList("a", "b", "c")));
}
private boolean checkArray(final List<String> a) {
return Optional //
.ofNullable(a) //
.map(List::stream) //
.orElseGet(Stream::empty) //
.filter(aa -> "a".equals(aa)) //
.findAny() //
.orElse(null)
!= null;
}
Not sure how you intend to use the info after, but if you just need to see if the customer qualifies, you can try
boolean qualify = accounts.stream()
.filter(type -> type.getAccCd().equals("typeA") || type.getAccCd().equals("typeB"))
.findFirst()
.isPresent();
This will return true if the customer qualifies, otherwise false.
You can rearrange code like this
Optional.of(myRepo.findAcctByCustoNo(auth.getCustNo()))
.filter(.....)
.orElse(......);
Optional methods return Optional object, thus it allows method chaining.

RxJava: How to conditionally apply Operators to an Observable without breaking the chain

I have a chain of operators on an RxJava observable. I'd like to be able to apply one of two operators depending on a boolean value without "breaking the chain".
I'm relatively new to Rx(Java) and I feel like there's probably a more idiomatic and readable way of doing this than my current approach of introducing a temporary variable.
Here's a concrete example, buffering items from an observable if a batch size field is non-null, otherwise emitting a single batch of unbounded size with toList():
Observable<Item> source = Observable.from(newItems);
Observable<List<Item>> batchedSource = batchSize == null ?
source.toList() :
source.buffer(batchSize);
return batchedSource.flatMap(...).map(...)
Is something like this possible? (pseudo-lambdas because Java):
Observable.from(newItems)
.applyIf(batchSize == null,
{ o.toList() },
{ o.buffer(batchSize) })
.flatMap(...).map(...)
You can use compose(Func1) to stay in-sequence but do custom behavior
source
.compose(o -> condition ? o.map(v -> v + 1) : o.map(v -> v * v))
.filter(...)
.subscribe(...)
You can also use filter operator with defaultIfEmpty if you wish to emit single value or switchIfEmpty if you wish to emit multiple values using another Observable.
val item = Observable.just("ABC")
item.filter { s -> s.startsWith("Z") }
.defaultIfEmpty("None")
.subscribe { println(it) }

Categories

Resources