Martin Marconcini
Martin Marconcini

Reputation: 27246

RXJava2 | Android: Filter arrayList and keep track of filter

Scenario: RXJava 2. Android.

Imagine you have an Observable from iterable like so: Observable.fromIterable(arrayList<Something>) and you need to do two things with it:

  1. Filter the items.

  2. Know if an item was filtered out (a.k.a.: the filter function returned false at least once).

This is a similar observable (extremely simplified to be relevant):

    final ArrayList<Something> someArrayList = getTheArrayList();

    Observable
            .fromIterable(someArrayList)
            .subscribeOn(Schedulers.computation())
            .filter(new AppendOnlyLinkedArrayList.NonThrowingPredicate<Something>() {
                @Override
                public boolean test(final Something something) {
                    return something.isValid();
                }
            })
            .toList()
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSuccess(new Consumer<List<Something>>() {
                @Override
                public void accept(@NonNull final List<Something> somethings) 
                    throws Exception {
                        // How can I tell if the filter above returned false
                        // at least once? 
                }
            })
            .subscribe();

To answer the above question, one option is to compare the original someArrayList to somethings. If they are different, well, something happened. But this means the list have to have the same items in the same order, which can be a problem if Something is a complicated object, that must implement an equals.

The plan B, which I am using and I don't like is to keep a "boolean array" outside the observable, like so:

final boolean[] hasInvalidData = new boolean[1];

and then in the .filter I can do:

    final isValid = something.isValid();
    if (!isValid) {
        hasInvalidData[0] = true;
    }
    return isValid;

and in success, I could simply do:

    if (hasInvalidData[0]) {
        // Something has been filtered
    }

The question is: is there a better approach?

UPDATE:

What I have done so far is simply compare originalList.size() with finalEmitedList.size(). If they are different, that means my filter "filtered" something.

Upvotes: 1

Views: 3333

Answers (2)

Ignacio Tomas Crespo
Ignacio Tomas Crespo

Reputation: 3571

not sure it works but Observable.sequenceEqual could work:

ArrayList<Object> list = ...
Predicate myFilter = ...
Observable<Object> observable = Observable.fromIterable(list);
Observable.sequenceEqual(observable, observable.filter(myFilter))
    .subscribe(new Consumer<Boolean>() {
        @Override
        public void accept(@NonNull Boolean changed) throws Exception {
            // result here
        }
    });

Upvotes: 1

Tassos Bassoukos
Tassos Bassoukos

Reputation: 16152

The way that I read it, this smells like an XY problem. If you don't need to keep track of the elements and you just need to compute until you find one element, everything becomes much easier:

 Observable
 .fromIterable(someArrayList)
 .subscribeOn(Schedulers.computation())
 .map(something -> something.isValid())
 .filter(bool -> bool)
 .first(false);

If you actually need the list of elements:

 Observable<Something> source = Observable
   .fromIterable(someArrayList)
   .subscribeOn(Schedulers.computation())
   .publish()
   .autoConnect(2);

source
.map(something -> something.isValid())
.reduce(false, (a,b) -> a | b)
.zipWith(source.toList(), (flag, list) -> {
   // do your stuff
})
.subscribe();

Upvotes: 2

Related Questions