I have an angular service where I want to expose an observable which acts as logical 'or' of sorts. Say I have multiple source sequences which can give on
and off
values. I need to output true
any time anything is on
, and go back to false
when all values are off
.
Example:
const replaceInput = input => input === 'on' ? 'off' : 'on';
const getSource = (name) => Observable.interval(1000)
.scan(
input => Math.random() > 0.5 ? replaceInput(input) : input,
'off' // initial value
)
.do(val => console.log(`${name}: ${val});
const first$ = getSource('first');
const second$ = getSource('second');
const result$ = ???;
Now, I've tried with result$ = Observable.merge(first$, second$)
, but that gives always on
. I have also tried with result$ = combineLatest(first$, second$)
, so that is somewhat ok, but my inputs are out of sync, so it's not always perfect. I mean, first$ may emit more than one value while second may never fire and the other way around. What else can I do?
I've thought of some mergeMap or similar, and then keeping the state on the outside context, that way I run updates on any event. But then I get into a problem, an 'off' event might mean 'off', but only if other sequences are also 'off', so it gets tricky quickly.
You actually gave the answer basically yourself:
const result$ = Observable.combineLatest(first$, second$)
.map(values => values.some(value => value === 'on'));
so that is somewhat ok, but my inputs are out of sync, so it's not always perfect.
I don't understand what you mean, to be honest. You stated clearly in your question that you want this:
I need to output true any time anything is on.
So if the inputs change with some time delay in between, you will have an intermediate result state. That's perfectly consistent with your question.
If you want to somehow wait for all inputs to change because they might change around the same time, you can add a debounceTime
to it:
const result$ = Observable.combineLatest(first$, second$)
.debounceTime(100)
.map(values => values.some(value => value === 'on'));
Or if you want to suppress the false
emissions, just filter them:
const result$ = Observable.combineLatest(first$, second$)
.map(values => values.some(value => value === 'on'))
.filter(Boolean); // sneaky trick; you can also use "value => value"
You can of course combine both.