Let's say I have one observable that emits at an interval of 1 sec. I have another observable that emits at an interval of 3 secs. The second observable emits a sequence of numbers. So every time that the second observable emits a sequence of numbers I want it to be zipped with the first observable.
For example:
observable 1
observable 2
zipped
1
[1]
2
[2]
3
[10,20,30,40]
[3, 10]
4
[4, 20]
5
[5, 30]
6
[10,20,30,40]
[6, 40, 10]
7
[7, 20]
If I am not wrong you are searching solution like this:
import { timer, combineLatest, Observable, BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
const first$ = timer(1000, 1000);
const second$ = new Observable<number[]>( subscriber => {
const intervalId = setInterval( () => {
subscriber.next([10,20,30,40]);
}, 3000)
return () => {
clearInterval(intervalId)
}
});
// This BehaviorSubject is used like
// some kind of "state" and keeps the emited
// array.
const state$ = new BehaviorSubject<number[]>([]);
second$.subscribe(state$);
combineLatest<[number,number[]]>([
first$,
state$
]).pipe(
map(([first, second]) => {
if (second.length > 0) {
return [first, second.shift()]
}
return [first]
})
).subscribe(
(value) => {
console.log(value);
}
);
Or at least your table describes it. Here is working stackblitz url:
https://stackblitz.com/edit/typescript-ufwqyh?file=index.ts
I've two lists with two distinct objects that need to be converted into the same type, the "second" list will be used only if the "first" list is empty, I tried to use the method defaultIfEmpty but it never return the second option.
const first = []; // could be [{code: 1}, {code: 2}]
const second = [{id: 1}, {id: 2}]
of(first).pipe(
map((value) => {number: value.code})
).pipe(
defaultIfEmpty(of(second).pipe(map((value) => {number: value.id})))
).subscribe(doSomething);
The desired output is:
[{number: 1}, {number: 2}]
On the example above, the map from defaultIfEmpty is never called;
how can I "switch" to another method source if the given source is empty?
will subscribe method be called after the map is complete, or it will be called for each item on map?
like this ?
const first = []; // could be [{code: 1}, {code: 2}]
const second = [{id: 1}, {id: 2}];
of(first).pipe(
filter(({length}) => length > 0),
defaultIfEmpty(second),
map((arr) => arr.map((x) => ({number: x.code ?? x.id})))
).subscribe(...);
If that's an option just create the right observable at runtime:
const makeObservable =
(arr1, arr2) =>
from(arr1.length ? arr1 : arr2)
.pipe(map(({code, id}) => ({number: code ?? id})));
const obs1$ = makeObservable([], [{id:1},{id:2}]);
const obs2$ = makeObservable([{code:2},{code:3}], []);
obs1$.subscribe(o => console.log(o));
obs2$.subscribe(o => console.log(o));
<script src="https://unpkg.com/rxjs#%5E7/dist/bundles/rxjs.umd.min.js"></script>
<script>
const {from} = rxjs;
const {map} = rxjs.operators;
</script>
const list1: { code: number }[] = [];
const list2 = [{ id: 1 }, { id: 2 }];
of(list1)
.pipe(
map((aList) => aList.map((v) => ({ 'number': v.code }))),
map((listModified) => {
return listModified?.length > 0
? listModified
: list2.map((value) => ({ number: value.id }));
})
)
.subscribe(console.log);
Don't get confused with map, one of them is from rxjs, the other is a function for arrays. In your first map, you are mapping the the whole array, not each element.
subscribe will be called when everything completes within the pipe.
Using RxJS Id like to emit each array item on an event/click.
I have below which works fine, but is there a cleaner way?
const testEventClick = new Subject();
const array = [1, 2, 3, 4, 5];
from(array)
.pipe(
concatMap(val => {
return new Observable(sub => {
testEventClick.subscribe(x => {
sub.next(val);
sub.complete();
});
});
})
)
.subscribe(console.log);
testEventClick.next();
testEventClick.next();
I would do the other way around and observe the subject.
Example: https://stackblitz.com/edit/rxjs-l4ttzh
Is there an operator that can let me map just the first emit ?
something like
import { from } from 'rxjs';
import { mapFirst } from 'rxjs/operators';
const source = from([1, 2, 3, 4, 5]);
const example = source.pipe(mapFirst(val => val + 10));
//output: 11,2, 3, 4, 5
If you want to write a user-land operator to do it:
import { OperatorFunction } from "rxjs";
import { map } from "rxjs/operators";
function mapFirst<T, R>(selector: (value: T) => R): OperatorFunction<T, T | R> {
return map<T, T | R>((value, index) => (index === 0) ? selector(value) : value);
}
And you'd use it as you have in your question.
Use first operator or take(1)
const source = from([1, 2, 3, 4, 5]);
const example = source.pipe(first());
I'm trying to emit simple array values one after another with 500ms in between:
var a = Rx.Observable.from([1,2,3]);
a.interval(500).subscribe(function(b) { console.log(b); });
However, this throws an exception:
Uncaught TypeError: a.interval is not a function.
Three ways to do it, with RxJS version 6 :
1. Using concatMap
import { from, of, pipe } from 'rxjs';
import { concatMap, delay } from 'rxjs/operators';
const array = [1, 2, 3, 4, 5];
from(array)
.pipe(
concatMap(val => of(val).pipe(delay(1000))),
)
.subscribe(console.log);
2. Using zip and interval
import { from, pipe, interval } from 'rxjs';
import { delay, zip} from 'rxjs/operators';
const array = [1, 2, 3, 4, 5];
from(array)
.pipe(
zip(interval(1000), (a, b) => a),
)
.subscribe(console.log);
3. Using interval as source
import { interval, pipe } from 'rxjs';
import { map, take } from 'rxjs/operators';
const array = [1, 2, 3, 4, 5];
interval(1000)
.pipe(
take(array.length),
map(i => array[i])
)
.subscribe(console.log);
As already pointed out by xgrommx, interval is not an instance member of an observable but rather a static member of Rx.Observable.
Rx.Observable.fromArray([1,2,3]).zip(
Rx.Observable.interval(500), function(a, b) { return a; })
.subscribe(
function(x) { document.write(x + '<br \>'); },
null,
function() { document.write("complete"); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.min.js"></script>
This is how I would do it:
var fruits = ['apple', 'orange', 'banana', 'apple'];
var observable = Rx.Observable.interval(1000).take(fruits.length).map(t => fruits[t]);
observable.subscribe(t => {
console.log(t);
document.body.appendChild(document.createTextNode(t + ', '));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.min.js"></script>
var arrayList = [1,2,3,4,5];
var source = Rx.Observable
.interval(500/* ms */)
.timeInterval()
.take(arrayList.length);
source.subscribe(function(idx){
console.log(arrayList[idx]);
//or document.write or whatever needed
});
Pretty late but a simpler solution would be :
const arr = ["Hi,", "how", "may", "I", "help", "you?"];
Rx.Observable.interval(500)
.takeWhile(_ => _ < arr.length)
.map(_ => arr[_])
.subscribe(_ => console.log(_))
I find Weichhold technique to be the best but that it would gain in clarity of intent by extracting the zipped value outside of the zip:
// assume some input stream of values:
var inputs = Obs.of(1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8);
// emit each value from stream at a given interval:
var events = Obs.zip(inputs, Obs.interval(1000))
.map(val => val[0])
.forEach(console.log);
If you want to release samples over time, you can do something like this
const observable = interval(100).pipe(
scan((acc, value) => [value, ...acc], []),
sampleTime(10000),
map((acc) => acc[0])
);
I had a little different requirement, my array kept updating over time too. So basically I had to implement a queue which I can dequeue at a regular interval, but I didn't want to use an Interval.
If somebody has a need for something like this then probably this solution can help:
I have a function createQueue() that takes the array as an input and returns an Observable which we subscribe for listening to events from the Array at a regular interval.
The function also modifies the 'push()' method of the passes array so that whenever any item is pushed in the array, the Observable would emit.
createQueue(queue: string[]) {
return Observable.create((obs: Observer<void>) => {
const arrayPush = queue.push;
queue.push = (data: string) => {
const returnVal = arrayPush.call(queue, data);
obs.next();
return returnVal;
}
}).pipe(switchMap(() => {
return from([...queue])
.pipe(
concatMap(val => of(val)
.pipe(delay(1000)))
);
}), tap(_ => queue.shift()))
}
Lets say that the array is: taskQueue = [];
So, we need to pass it to the above function and subscribe to it.
createQueue(taskQueue).subscribe((data) => {
console.log('Data from queue => ', data);
});
Now, every time we do taskQueue.push('<something here>'), the subscription will trigger after a delay of "1000ms".
Please note: we should not be assigning a new array to the taskQueue after createQueue() has been called, or else we will loose the modified push().
Here is a dummy example for the above implementation: Test Example
Rx.Observable instance doesn't have interval method http://xgrommx.github.io/rx-book/content/core_objects/observable/observable_instance_methods/index.html. You can use like this.
Rx.Observable.interval(500)
.map(function(v) { return [1,2,3];})
.subscribe(console.log.bind(console));