DebounceTime Rxjs when same parameters being passed in - javascript

I have multiple components using this service and I want to debounceTime when I'm piping in the same value but if it's a different value, let it through. In this code, I want to somehow check if a types been used before
from(types).pipe(
debounceTime(1000),
map(([data, type]) => {
// mapping logic
}),
)
It very similar to this issue https://github.com/lodash/lodash/issues/2403 and I'm trying to understand it but I'm hoping to used RXJS code still
Thanks

One possible solution could be to use the pairwise operator to emit both the previous and current values, then use the debounce operator to compare the values and debounce accordingly:
from(types).pipe(
pairwise(),
debounce(([prev, cur]) => (prev === cur)
? timer(1000)
: EMPTY
),
map(([, cur]) => cur), // No further need for prev value
map(([data, type]) => {
// mapping logic
}),
)

Related

Javascript : skip calling the chaining methods based on a condtion dynamically

I have an array of objects, I call a series of methods on the array. I wanted to skip in between methods based on a condition. Is there a generic way to handle it instead of having an if/else block?
When a filter is applied i wanted to call filter method in this array, when filter is not applied i want skip that.
// when filter is applied
salesActivityTypes
.filter(s=>s.selected)
.map(key => key.name);
//when filter not applied
salesActivityTypes
.map(key => key.name);
I don't want to use something like if/else block and copy-pasting the same code twice. Is there any better way to handle this?
The order of methods call matters and should'nt change
You don't need an if/else block, and if/else expression is enough and can be part of your chain:
(filteringActive
? salesActivityTypes.filter(s => s.selected)
: salesActivityTypes
).map(key => key.name);
Alternatively, in this particular case you can also pass a no-op callback to filter:
salesActivityTypes
.filter(filteringActive ? (s => s.selected) : (_ => true))
.map(key => key.name);
Just add another check inside the .filter callback for whether filtering is active.
salesActivityTypes
.filter(s => filteringActive ? s.selected : true)
.map(key => key.name);

recursive rxjs switchmap ish

I want a variable states$: an observable stream of objects, with each object containing a member nextState$ of type observable. This property nextState$ sends a unique item corresponding to the next state, and so on...
example:
const states$ = of({ nextState$: createObservableWithNextState$() }).pipe(
switchMap(state => state.nextState$),
switchMap(state => state.nextState$),
switchMap(state => state.nextState$),
switchMap(state => state.nextState$),
...
)
of course it doesn't work, for two reasons at least:
I don't want the ... infinite repetition of switchMap in my code
I want to subscribe to state$ and receive each object (including the initial one in the of())
Of course I could create my own observable from scractch but before I would like to known if it would be possible with existing rxjsoperators. Any idea ?...
RxJS#expand
Expand should do what you're after pretty simply.
I assume at some point you'll reach a state without a nextState$, but you can change that condition easily.
const states$ = of({
nextState$: createObservableWithNextState$()
}).pipe(
expand(state => state.nextState$ != null? state.nextState$ : EMPTY)
);
Expand is closer to mergeMap than switchMap. You can set concurrent to 1 to make it work like concatMap. If you're really after a switchMap-like behaviour, this gets a bit more complicated.

How to get rid of multiple nested switchMap with early returns

I got 3 endpoints that returns upcoming, current, past events. I should show only the one that is the farthest in the future. To optimize the calls and not to call all the endpoints at once.I have written a simple RxJs stream where I call the first endpoint and if it does not return data I call second and so on. The code looks like this:
this.eventsService.getUpcoming(id).pipe(
switchMap((upcoming) => {
if (upcoming.length) {
return of(upcoming);
}
return this.eventsService.getCurrent(id).pipe(
switchMap((current) => {
if (current.length) {
return of(current);
}
return this.eventsService.getPast(id)
})
);
}),
// some other pipe operators map etc.
It is possible not to have nested switch map within a switch map?
I think you could use just concat() to make the calls sequential and then take(1) and skipWhile() to automatically complete when the first useful response arrives:
concat(
this.eventsService.getUpcoming(id),
this.eventsService.getCurrent(id),
this.eventsService.getPast(id)
).pipe(
skipWhile(response => response.length === 0),
defaultIfEmpty([]),
take(1),
);
take(1) will complete the chain when the first item in skipWhile doesn't match the condition.
Try something like this
this.eventsService.getUpcoming(id).pipe(
switchMap((upcoming) => {
if (upcoming.length) {
return of(upcoming);
}
return this.eventsService.getCurrent(id)
},
switchMap((current) => {
if (current.length) {
return of(current);
}
return this.eventsService.getPast(id)
})
)
This way you do not nest the switchMap
You can use concat operator to create an observable which sequentially emits values from each observable. Pipe the results to the find operator that will return the result from the first result that meets the condition and complete the observable. This will prevent subsequent observables to be executed from the stream created by concat.
Difference between first and take
One side effect of find that I think you will find useful for your example is that if no conditions are met, then the last result is still emitted. This is different then using an operator like first which will throw an error if the source observable completes without a match, or take which won't emit anything since a prior operator would be used for filtering emissions.
So in your case you'll at least get an empty array if all responses are empty.
concat(
// try each request in order.
this.eventsService.getUpcoming(id),
this.eventsService.getCurrent(id),
this.eventsService.getPast(id)
).pipe(
// emits the first result that meets the condition.
find(response => response.length > 0)
);

combineLatest refactoring for #deprecated — resultSelector no longer supported, pipe to map instead?

The angular material documentation app has the following snippet in it:
// Combine params from all of the path into a single object.
this.params = combineLatest(
this._route.pathFromRoot.map(route => route.params),
Object.assign);
TSLint strikesout combineLatest with this message:
#deprecated — resultSelector no longer supported, pipe to map instead
How should that be fixed?
Also if you understand the technique being used, a simple example with elaboration would be awesome.
Here's a code link:
https://github.com/angular/material.angular.io/blob/master/src/app/pages/component-category-list/component-category-list.ts
combineLatest(observables, resultSelector)
can often be replaced with
combineLatest(observables).pipe(
map(resultSelector)
)
But whether this works in the same way depends on what parameters your resultSelector accepts. combineLatest(observables) emits an array and RxJs automatically spreads this array when you're using the deprecated resultSelector.
return isArray(args) ? fn(...args) : fn(args); // fn is your resultSelector
As Object.assign returns different values depending on whether you provide an array or multiple values you have to spread the array manually.
combineLatest(observables).pipe(
map(items => Object.assign({}, ...items))
)
The result selector is just a function that can be used to change the shape of the output.
So, you can simply omit the result selector param and use a map to transform the shape:
this.params = combineLatest(
this._route.pathFromRoot.map(route => route.params)
).pipe(
map(Object.assign)
);
A common use the of the result selector (for me anyway) was to return the array as an object with named properties, so something like this:
// Old Way
myObs$ = combineLatest(
[src1$, src2$, src3$],
([src1, src2, src3]) => ({src1, src2, src3})
);
// New Way
myObs$ = combineLatest(src1$, src2$, src3$)
.pipe(
map(([src1, src2, src3]) => ({src1, src2, src3}))
);
pipe the result and use the map operator instead, something like:
combineLatest(this._route.pathFromRoot.map(route => route.params)).pipe(
map(Object.assign)
);
Reference
Also, not sure about the intent of the code, since it seems as though it's calling Object.assign on a new array reference, not sure why that's necessary, but that's not entirely relevant.

How can I emit values with delay in RXJS?

I have an array of values [1,2,3] .
I want to emit each value with delay
I've managed to do it with the zip operator :
Rx.Observable.from([1,2,3])
.zip(Rx.Observable.timer(0, 1000), x => x)
.subscribe((e) => console.log(e));
Question:
Is there any more appropriate operator for such task ? Involving an inner observable seems ( to me) incorrect approach.
Should I unsubscribe the inner Observable manually ? Becuase basically no one tells it to stop.
jsbin
You can delay each emission itself and wait until the previous one completed. Like this for example:
Rx.Observable.from([1,2,3])
.concatMap(x => Observable.of(x).delay(1000)) // or Observable.timer(1000).mapTo(x)
.subscribe((e) => console.log(e));
If you want to use zip you don't need to unsubscribe the timer but you need to tell it to complete (for example with take() or takeUntil()).

Categories