jclova
jclova

Reputation: 5576

From RxJava2, How can I compare and filter two observables if the values are equal?

I am new to RxJava2.

I am trying to get a list of Transaction object both from cache and from server. I want to compare the server value to cache value and if the server value is the same, then ignore it.

I was able to do it easily using .scan() because we can return null and when null is returned from the .scan() the value got ignored(filtered).

RxJava 1

private Observable<List<Transaction>> getTransactionsFromCacheAndServer() {
    return Observable.concat(
            getTransactionsFromCache(),
            getTransactionsFromServer()
    )
            .scan((p1, p2) -> {
                if (p1 == null && p2 != null) {
                    return p2;
                } else if (p1 != null && !isListSame(p1, p2)) {
                    return p2;
                } else {
                    return null;
                }
            });
}

With RxJava 2, since I cannot return null anymore, things are not easy.

RxJava 2

    private Observable<List<Transaction>> getTransactionsFromCacheAndServer() {
    return Observable.concat(
            getTransactionsFromCache(),
            getTransactionsFromServer()
    )
            .map(FilterObject::new)
            .scan((filterObject1, filterObject2) -> {
                List<Transaction> p1 = (List<Transaction>)filterObject1.value;
                List<Transaction> p2 = (List<Transaction>)filterObject2.value;

                if (p1.size() == 0 && p2.size() > 0) {
                    return filterObject2;
                } else if (!isListSame(p1, p2)) {
                    return filterObject2;
                } else {
                    filterObject2.filter = true;
                    return filterObject2;
                }
            })
            .filter(filterObject -> !filterObject.filter)
            .map(filterObject -> (List<Transaction>)filterObject.value);
}

Where FilterObject is:

public class FilterObject {
public Object value;
public boolean filter;

public FilterObject(Object value) {
    this.value = value;
}

}

Even though I can achieve the same thing using above method, it seems very ugly. Also I had to include two maps which might not be so performance friendly.

Is there a simple/clean way to achieve what I want?

Upvotes: 0

Views: 587

Answers (2)

jclova
jclova

Reputation: 5576

Based on andras' answer, I modified little bit to achieve what I want.

private Observable<List<String>> getTransactionsFromCacheAndServer() {
    return Observable.concat(
        getTransactionsFromCache(),
        getTransactionsFromServer()
    )
        .filter(list -> !list.isEmpty())
        .distinctUntilChanged(this::isListSame)
        .switchIfEmpty(Observable.just(new ArrayList<>()));

}

Andreas' answer will always receive an empty list and then a real data.

My solution above will receive:
1. Data from cache (and then data from server if different)
2. Empty list if both cache and server returns Empty list.

Upvotes: 1

andras
andras

Reputation: 3655

I don't think there is a generic solution to this problem, since an empty list and a list that needs to be filtered (which happens to be empty in all cases) are two different things (the output of the scan) and needs to be handled differently.

However, in your particular case you never emit an empty list, except maybe for the first output.

(I am using String instead Transaction, shouldn't matter)

private Observable<List<String>> getTransactionsFromCacheAndServer() {
    return Observable.concat(
            getTransactionsFromCache(),
            getTransactionsFromServer()
    )
            .filter(list -> !list.isEmpty())
            // If you prefer a consistent empty list over the first
            // empty list emission getting filtered
            .startWith((List<String>) Collections.EMPTY_LIST)
            // Newly emitted value cannot be empty, it only depends only on the comparison
            .distinctUntilChanged(this::isListSame);
}

That's the closest I could get with as few operators as possible. Hope it solves your problem.

Upvotes: 2

Related Questions