How to use pipe operator in RxJs 5.5.6 - rxjs

I'm learning Rxjs operators and stuff. I understood map operator is used to transform the data. But with using pipe operator I'm unable to use the methods of Map operator. I am using Rxjs 5.5.6
I have an Observable like
const source = Observable.of("david");
And subscribing to that Observable and transforming that data to Upper case is written like
source.pipe(
map(x => x.toString().toUpperCase())
).subscribe(data => console.log(data));
But when I removed that toString() inside the map operator. I couldn't use the toUpperCase() anymore.I mean why I need to convert my data to string and then do an toUpper().
Could anyone please let me know what I'm missing.

The map operator was a method on the observable class in RxJs 5 so you didn't need pipe
source.map(x => x.toString().toUpperCase());
The reason the pipe operator was introduced was so that the operator functions could be tree shaken in RxJs 6.
const source = Rx.Observable.of('david');
source.map(x => x.toString().toUpperCase()).subscribe(data => console.log(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Why are you still using 5.5.6?
const { of } = rxjs;
const { map } = rxjs.operators;
const source = of('david');
source.pipe(
map(x => x.toString().toUpperCase())
).subscribe(data => console.log(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>

Related

is there a Better way rather than to chain subscribe inside a subscribe with an if condition

Is there a better way to re-write this code and avoid chaining of subscriptions ?
Why am I chaining? because I need to the output of source1$ in child subscriptions
And also I have if conditions because I want to call child subscriptions conditionally
PS i checked solution in this post
Here is the stackblitz link and code
import { from } from 'rxjs';
//emit array as a sequence of values
const source1$ = from([1]);
const source2$ = from([2]);
const source3$ = from([3]);
const useCond1 = true; // this is dynamic can be false too
const useCond2 = true; // this is dynamic can be false too
source1$.subscribe(val => {
if (useCond1) {
source2$.subscribe(() => {
console.log('val from source1 in source2', val);
});
}
if (useCond2) {
source3$.subscribe(() => {
console.log('val from source1 in source3', val);
});
}
});
Not sure, but it seems that you need switchMap or mergeMap and iif
from rxjx doc:
import { fromEvent, iif, of } from 'rxjs';
import { mergeMap, map, throttleTime, filter } from 'rxjs/operators';
const r$ = of(`I'm saying R!!`);
const x$ = of(`X's always win!!`);
fromEvent(document, 'mousemove')
.pipe(
throttleTime(50),
filter((move: MouseEvent) => move.clientY < 210),
map((move: MouseEvent) => move.clientY),
mergeMap(yCoord => iif(() => yCoord < 110, r$, x$))
)
.subscribe(console.log);
Yes, there is a better way!
RxJS provides many different operators and static functions for combining, filtering, and transforming observables. When you use what the library provides, you do not need to have nested subscriptions.
In general, I find it simpler to not do any logic at all inside the subscribe, but rather design observables that emit the exact data that is needed.
A simplistic example could look like this:
someValue$ = source1$.pipe(
switchMap(val1 => useCond1 ? source2$ : of(val1))
);
someValue$.subscribe();
switchMap will subscribe to an "inner observable" whenever it receives an emission. The logic above says to either return the value emitted from source1$ (val1) or return whatever source2$ emits depending on the value of useCond1.
So source2$ will only get subscribed to when useCond1 is true;
Note: the function inside switchMap should return an observable (because switchMap subscribes to it), so of was used to turn the emitted value into an observable.
In your case, let's assume you want to emit some calculated value, based possibly on the other two sources.
We can use combineLatest to create a single observable based on 3 different sources. Since you only want to optionally call source2$ and source3$, we can define the sources based on your conditions. We can then use map to transform the array of values from the 3 sources, into the desired output:
someValue$ = source1$.pipe(
switchMap(val1 => {
const s1$ = of(val1);
const s2$ = useCond1 ? source2$ : of('default val2');
const s3$ = useCond2 ? source3$ : of('default val3');
return combineLatest([s1$, s2$, s3$]);
}),
map(([val1, val2, val3]) => {
return ... // your logic to return desired value
})
);
combineLatest will emit an array containing the latest emissions from each source whenever any source emits. This means someValue$ will emit the latest calculated value whenever any of the sources change.

RxJS: SwitchMap for Array of Strings

Following use case: A user can join 0...* groups. Each group has an ID and contains 0...* posts.
I subscribe to an Observable (to get the groups of the user he joined) and this returns an array of strings (the group IDs).
const groupIds$ = of(['a', 'b', 'c']);
If I only had one I would now use switchMap and return this new observable and subscribe to it to get the posts from the group.
But so I have an array and so this isn't working. Does anyone has an idea which RxJS operator(s) can achieve this to get the posts from all groups?
Or does no operator for such use case exist and I have to do it separately at subscribe?
You can use ideally forkJoin if you know you'll have all source Observables as an array:
groupIds$.pipe(
concatMap(groups => {
const observables = groups.map(id => ...);
return forkJoin(...observables);
})
).subscribe(...);
forkJoin will emit a single array with all the results in the same order as in groupIds$.
Eventually if you don't care about the order and just want to get all the results in parallel you can use merge instead of forkJoin (I mean the "Observable creation method" called merge imported directly from rxjs. Not the merge operator from 'rxjs/operators').
Everyone who arrives at this question, here is the answer for "switchMap for Array of strings" (thanks to martin). You only have to use 'merge' from 'rxjs' (not the operator!). Inside switchMap return merge and you are done:
groupIds$.pipe(
switchMap(groups => {
const observables = groups.map(id => {
// your function that returns the observable
return something.getObservable(id);
});
return merge(...observables);
})
).subscribe(data => {
console.log('DATA', data);
});
Possible solution: https://www.learnrxjs.io/operators/transformation/mergemap.html.
const groupIds$ = of(['a', 'b', 'c']);
const myPromise = id =>
new Promise(resolve => resolve(something.getPostsByGroupId(id)));
const posts$ = groupIds$.pipe(
mergeMap(
id => myPromise(id),
(valueFromSource, valueFromPromise) => {
return valueFromPromise;
}));
posts$.subscribe(...);
Terser improvement on great existing answers. This emits an array of all posts each time the groupIds$ source emits.
groupIds$.pipe(
switchMap(ids => zip(...ids.map(id => service.getPosts(id)))),
map((posts: Array<Post[]>) => [].concat.apply([], posts))
).subscribe(posts => /*is an array of all posts*/);

Observable with inner observables

I have been working with Observables a bit, but can't figure out how to do the following:
I have an observable that emits an array of IDs.
I then want to retrieve these IDs, again using Observables.
Finally I want to put everything together in an Observable that
emits an array of the retrieved items.
I could put my code attempts here, but I don't think it would help a lot.
I'm using RxJS 5.5
You can use for that flattening operators for that (like mergeMap or concatMap depending on requirements). For example:
const { Observable, combineLatest, of, from } = rxjs; // = require("rxjs")
const { mergeMap, toArray, map } = rxjs.operators; // = require("rxjs/operators")
const ids = [0,1,2,3,4,5,6,7,8,9];
const transform = id => of(`${id}-transformed`);
of(ids).pipe(
mergeMap(ids => ids),
mergeMap(id => transform(id)),
toArray()
).subscribe(e => console.log(e))
<script src="https://unpkg.com/rxjs#6.2.2/bundles/rxjs.umd.min.js"></script>

RxJS merge observables but prefer one over the other

I have two observables A and B, and I'd like to create an observable which emits the latest value from B, but if B hasn't emitted anything yet, it emits the latest value from A. I was thinking something like this:
merge(A.takeUntil(B), B)
But this seems a little counterintuitive, and I'd like to have a more readable approach. Is there a more canonical Rx way to do this?
Custom Operator
This solution is very readable in use but complicated by the fact that you hide the complexity within a custom operator. The benefit of a custom operator is that you only subscribe to each source observable once. This means your observables don't have to be "hot".
Unfortunately, this operator breaks synchronously executing observables and has each value pass through the event loop.
function prefer<T, R>(...observables: Observable<R>[]): Observable<R>{
return new Observable(observer => {
const subscrptions = new Array<Subscription>();
const unsub = (index) => {
for(let i = index; i < subscrptions.length; i++){
subscrptions[i].unsubscribe();
}
}
observables
.map(stream => publish()(stream))
.forEach((stream, index) => {
subscrptions.push(stream.subscribe((payload: R) => {
observer.next(payload);
unsub(index + 1);
subscrptions.length = index + 1;
}));
stream.connect();
});
return { unsubscribe: () => unsub(0) }
})
}
Operator in Use
prefer(
interval(10000).pipe(map(_ => "Every 10,000")),
interval(5000).pipe(map(_ => "Every 5,000")),
interval(1000).pipe(map(_ => "Every 1,000")),
interval(250).pipe(map(_ => "Every 250"))
).subscribe(console.log);

switchMapTo upon source observable emission

The difference between switchMap and switchMapTo is that switchMap transforms each source emission into observable upon the emission and switchMapTo ignores emitted value and transforms each emission to an Observable that is built up during stream creation.
The thumb rule here is use switchMap when your inner stream depends on the source stream values and use switchMapTo when it doesn't.
But what if I don't care for the emission value but I do care for the emission time?
Meaning I'd like the inner Observable to be evaluated upon source Observable emission.
The obvious thing here is to use switchMap(() => Observable) but it just doesn't feel right, because of the thumb rule I mentioned before.
Example with switchMapTo (bad):
const predefinedKey = 'key';
//This need to be initialized
const obj = {};
function getObservable(key){
return Rx.Observable.of(obj[key]);
}
//This is initialization stream
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
//Would like to access the object only after initialization
const result = onInit.switchMapTo(getObservable(predefinedKey));
//Expect to see 1 in output but see 'undefined' because switchMapTo evaluated before the object is initialized
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Example with switchMap (good):
const predefinedKey = 'key';
//This need to be initialized
const obj = {};
function getObservable(key){
return Rx.Observable.of(obj[key]);
}
//This is initialization stream
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
//Would like to access the object only after initialization
const result = onInit.switchMap(() => getObservable(predefinedKey));
//Expect to see 1 in output
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
The examples are very artificial but they describe the situation pretty well.
What is the right approach here? Any other Observable function I can use for delayed execution?
Based on your example, you can use switchMapTo in combination with Observable.defer:
const predefinedKey = 'key';
const obj = {};
function getObservable(key){
return Rx.Observable.defer(() => Rx.Observable.of(obj[key]));
}
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
const result = onInit.switchMapTo(getObservable(predefinedKey));
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Instead of deferring in getObservable, you could also defer in the switchMapTo call:
const result = onInit.switchMapTo(Rx.Observable.defer(() => getObservable(predefinedKey)));
This will just depend on the situation. That said, I also don't think there's anything wrong with using switchMap and personally, I'd probably do that instead of deferring (which is useful in other situations).

Resources