5
votes

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:

  1. Try to use zip - I can't because second Observable can't be subscribed until the first one returns the MyObject

  2. 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 */
      );
    
  3. 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)?

3
objectDAO.getById(objectId).flatMap(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. - david.mihola
@david.mihola it's similar to mine 3rd method, except that now I've realized that I don't need to use blocking get. So rather that returning Pair<Boolean, MyObject I can check the boolean and return Maybe.just(myObject) or Maybe.empty() / Maybe.error() Still not sure if it's a good solution. - LR89
Also, it seems I can't use objectDAO.flatMap(otherDao.map()) combination, and when I change it to objectDAO.map(otherDao.map()) then the result is Single<Maybe<Pair<Boolean, MyObject>>> which is also not so nice - LR89
Yeah, sorry - it's called flatMapSingle! - david.mihola
Yeah, I also found that I can use flatMapSingle instead. Now I have other issue, but not related to the original question. Please paste your full answer below so I can accept it :) - LR89

3 Answers

5
votes

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.

1
votes

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

0
votes

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 -> ...)