Last active
February 8, 2024 14:44
-
-
Save Ahmed-Adel-Ismail/93b3e6f2385d2c7fa1c1a07b6244fc60 to your computer and use it in GitHub Desktop.
Observable Operators that enables switch-case and if-else in RxJava stream, you can use them with lift() operator, and pass to them a Map of key/function , if the key is matched with the emitted item, it's function will be executed and it's value will be returned in the stream
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* an {@link ObservableOperator} that simulates an if-else in an RxJava Stream, it takes a {@link Map} | |
* of {@link Predicate} as key and a {@link Function} as value ... the if any emitted item passes | |
* the {@link Predicate}, this emitted item will be passed to the {@link Function} mapped to it, | |
* and this item will be invoked and it's result will continue down the stream | |
* <p> | |
* sample code : | |
* <p> | |
* {@code List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);}<br> | |
* {@code Map<Predicate<Integer>, Function<Integer, String>> blocks = new LinkedHashMap<>(2)}<br> | |
* {@code blocks.put(i -> i % 2 == 0, i -> "Even number : " + i);}<br> | |
* {@code blocks.put(i -> i % 2 != 0, i -> "Odd number : " + i);}<br> | |
* <p> | |
* {@code Observable.fromIterable(list)}<br> | |
* {@code .lift(new IfElse<>(blocks))}<br> | |
* {@code .subscribe(System.out::println);}<br> | |
* <p> | |
* // result :<br> | |
* Odd number : 1<br> | |
* Even number : 2<br> | |
* Odd number : 3<br> | |
* Even number : 4<br> | |
* Odd number : 5<br> | |
* Even number : 6<br> | |
* Odd number : 7<br> | |
* Even number : 8 | |
*/ | |
public class IfElse<T, R> implements ObservableOperator<R, T> { | |
private final Map<Predicate<T>, Function<T, R>> blocks = new LinkedHashMap<>(); | |
/** | |
* create a {@link IfElse} operator, if any {@link Function} returned {@code null}, the | |
* whole operation will crash | |
* | |
* @param blocks the map that holds {@link Function} that are the if-else blocks | |
*/ | |
public IfElse(@NonNull Map<Predicate<T>, Function<T, R>> blocks) { | |
if (blocks != null) { | |
this.blocks.putAll(blocks); | |
} | |
} | |
@Override | |
public Observer<? super T> apply(final Observer<? super R> observer) throws Exception { | |
return createResultObserver(observer); | |
} | |
private Observer<T> createResultObserver(final Observer<? super R> observer) { | |
return new Observer<T>() { | |
@Override | |
public void onSubscribe(@NonNull Disposable d) { | |
} | |
@Override | |
public void onNext(@NonNull T emittedItem) { | |
List<Function<T, R>> validBlocks = Observable.fromIterable(blocks.keySet()) | |
.filter(key -> key.test(emittedItem)) | |
.map(blocks::get) | |
.toList() | |
.flatMapMaybe(Maybe::just) | |
.blockingGet(); | |
if (validBlocks == null) { | |
return; | |
} | |
try { | |
for (Function<T, R> block : validBlocks) { | |
invokeOnNext(observer, block.apply(emittedItem)); | |
} | |
} catch (Throwable e) { | |
onError(e); | |
} | |
} | |
@Override | |
public void onError(@NonNull Throwable e) { | |
observer.onError(e); | |
} | |
@Override | |
public void onComplete() { | |
observer.onComplete(); | |
} | |
}; | |
} | |
private void invokeOnNext(Observer<? super R> observer, R onNextValue) { | |
observer.onNext(onNextValue); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* An {@link ObservableOperator} that enables the switch-case behavior in Rx-Java, it uses | |
* a table look up technique to simulate a switch-case behavior | |
* <p> | |
* you must supply a {@link Map} that holds functions that will be executed if the emitted | |
* item matches the key they are mapped to | |
* <p> | |
* if multiple emitted items matches several keys, all there functions will be executed and | |
* will emit there results, if you want the first match to disable the rest of the checks, | |
* you can use {@link SwitchCaseBreak} instead | |
* <p> | |
* sample code : | |
* <p> | |
* {@code List<Integer> list = Arrays.asList(1, 2, 3, 4);}<br> | |
* {@code Map<Integer, Function<Integer, String>> caseBlocks = new HashMap<>(2);}<br> | |
* {@code caseBlocks.put(2, (i) -> "TWO SELECTED");}<br> | |
* {@code caseBlocks.put(3, (i) -> "THREE SELECTED");}<br> | |
* <p> | |
* {@code Observable.fromIterable(list)}<br> | |
* {@code .lift(new SwitchCase<>(caseBlocks))}<br> | |
* {@code .subscribe(System.out::println);}<br> | |
* <p> | |
* // result :<br> | |
* TWO SELECTED<br> | |
* THREE SELECTED<br> | |
* | |
* @param <T> the type of the items that will be passed to the {@code switch} statement | |
* @param <R> the type of the items that will be returned from the {@code case} block execution | |
*/ | |
public class SwitchCase<T, R> implements ObservableOperator<R, T> { | |
final Map<T, Function<T, R>> caseBlocks = new LinkedHashMap<>(); | |
/** | |
* create a {@link SwitchCase} operator, if any {@link Function} returned {@code null}, the | |
* whole operation will crash | |
* | |
* @param caseBlocks the map that holds {@link Function} that are the case-blocks in this | |
* switch-case | |
*/ | |
public SwitchCase(@NonNull Map<T, Function<T, R>> caseBlocks) { | |
if (caseBlocks != null) { | |
this.caseBlocks.putAll(caseBlocks); | |
} | |
} | |
@Override | |
public Observer<? super T> apply(final Observer<? super R> observer) throws Exception { | |
return createResultObserver(observer); | |
} | |
private Observer<T> createResultObserver(final Observer<? super R> observer) { | |
return new Observer<T>() { | |
@Override | |
public void onSubscribe(@NonNull Disposable d) { | |
} | |
@Override | |
public void onNext(@NonNull T switchOn) { | |
Function<T, R> block = Maybe.just(caseBlocks) | |
.filter(map -> map.containsKey(switchOn)) | |
.map(map -> map.get(switchOn)) | |
.blockingGet(); | |
if (block == null) { | |
return; | |
} | |
try { | |
invokeOnNext(observer, block.apply(switchOn)); | |
} catch (Throwable e) { | |
onError(e); | |
} | |
} | |
@Override | |
public void onError(@NonNull Throwable e) { | |
observer.onError(e); | |
} | |
@Override | |
public void onComplete() { | |
observer.onComplete(); | |
} | |
}; | |
} | |
void invokeOnNext(Observer<? super R> observer, R onNextValue) { | |
observer.onNext(onNextValue); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* same as {@link SwitchCase} but it emits only one item which is the result of the | |
* {@link Function} mapped to the key matching the first emitted value | |
* <p> | |
* sample code : | |
* <p> | |
* {@code List<Integer> list = Arrays.asList(1, 2, 3, 4);}<br> | |
* {@code Map<Integer, Function<Integer, String>> caseBlocks = new HashMap<>(2);}<br> | |
* {@code caseBlocks.put(2, (i) -> "TWO SELECTED");}<br> | |
* {@code caseBlocks.put(3, (i) -> "THREE SELECTED");}<br> | |
* <p> | |
* {@code Observable.fromIterable(list)}<br> | |
* {@code .lift(new SwitchCaseBreak<>(caseBlocks))}<br> | |
* {@code .subscribe(System.out::println);}<br> | |
* <p> | |
* // result :<br> | |
* TWO SELECTED<br> | |
*/ | |
public class SwitchCaseBreak<T, R> extends SwitchCase<T, R> { | |
public SwitchCaseBreak(Map<T, Function<T, R>> caseBlocks) { | |
super(caseBlocks); | |
} | |
@Override | |
void invokeOnNext(Observer<? super R> observer, R onNextValue) { | |
super.invokeOnNext(observer, onNextValue); | |
caseBlocks.clear(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment