Hellcaraxe
Hellcaraxe

Reputation: 65

rxJava better way to wait on a specific sequence of values

I wondered what would be the proper way to filter for a certain sequence of values coming through a observable pipe. For example:

I had a problem where I needed to do something after the pipe becomes false but only directly after being true.

Observable x = Observable.just(false, false, false, true, false);

My first approach:

x
.filter(state -> state)
.switchMap(x)
.filter(state -> !state)
.subscribe(x -> doSomething())


Second approach:

private boolean globalState = false;


private void someFunction() {
   x.map(this::hasTurnedOffAgain)
   .filter(state -> state)
   .subscribe(doSomething)
}

...

private boolean hasTurnedOggAgain(Boolean state) {
    boolean hasTurnedOff= !state && peeping;
    globalState = state;
    return hasFinished;
}

I had a third version using the scan operator but I think it was to complex for this simple problem.

Is there a better way to implement something like this? And even more complex sequences?

Thank you and have a great day!

Upvotes: 0

Views: 446

Answers (2)

Progman
Progman

Reputation: 19555

You can use the buffer() method to build an observable which have the previous and current item from the original observable. Use a count value of 2 as you want to check two adjacent values and a skip value of 1 as you want to go a single step forward.

Observable<Boolean> original = Observable.just(false, false, false, true, false, false, true, true, false);

Observable<List<Boolean>> buffered = original.buffer(2, 1);

buffered.subscribe(new Consumer<List<Boolean>>() {

    @Override
    public void accept(List<Boolean> t) throws Exception {
        System.out.println("Direct:"+t);
    }
});

This will generate the following output:

Direct:[false, false]
Direct:[false, false]
Direct:[false, true]
Direct:[true, false]
Direct:[false, false]
Direct:[false, true]
Direct:[true, true]
Direct:[true, false]
Direct:[false]

You can use this new observable and use filter() to get only these values where the previous entry was true and the current entry is false, so you see a "falling edge":

Observable<List<Boolean>> filtered = buffered.filter(l -> l.size() == 2 &&
                                                          l.get(0) == true && 
                                                          l.get(1) == false);

filtered.subscribe(new Consumer<List<Boolean>>() {

    @Override
    public void accept(List<Boolean> t) throws Exception {
        System.out.println("Filtered: "+t);
    }
});

This will generate the following output:

Filtered: [true, false]
Filtered: [true, false]

Upvotes: 1

Alexei Kaigorodov
Alexei Kaigorodov

Reputation: 13535

This is the case when you need a class to save state and not just functions.

class FilterClass {
    Boolean prev = null;

    boolean filter(Boolean current) {
        // detect false but only directly after being true.
        boolean res = (current==Boolean.FALSE) && (prev==Boolean.TRUE);
        prev = current;
        return res;
    }
}

Observable<Boolean> x = Observable.just(false, false, false, true, false);
FilterClass filter=new FilterClass();

x.filter(filter::filter)
 .subscribe(x -> doSomething(x));

Upvotes: 0

Related Questions