Reputation: 65
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
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
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