I have this operator function multiply
that I would like to be able to pass the source observable to and a input value within the source. I can achieve this with a switchMap
but then I also need to pass the source to the operator which seems a bit redundant.
source$ = of({
val: 5,
factor: 6,
factorTwo: 3
});
multiply(factor: number) {
return (source: Observable<{ val: number }>) => source.pipe(map(value => value.val * factor))
}
simpleUsage$ = this.source$.pipe(this.multiply(1))
WithFactorOne$ = this.source$.pipe(
switchMap(x => this.multiply(x.factor)(of(x)))
)
WithFactorTwo$ = this.source$.pipe(
switchMap(x => this.multiply(x.factorTwo)(of(x)))
)
If I use map
the source is passed, but the return type is (source: Observable<{val: number;}>) => Observable<number>
rather than Observable<Number>
like I get when using switchMap
. Is it possible to achieve this without passing the source manually?
desieredFactor$ = this.source$.pipe(
map(x => this.multiply(x.factor))
)
This is a simple example but the general issue is shown. Should I rethink the solution? Can this be accomplished in another way?
CodePudding user response:
You could allow passing a function to multiply
, which would then allow you to use any value from the emitted source:
interface Example {
val: number;
factor: number;
factorTwo: number;
}
source$ = of<Example>({
val: 5,
factor: 6,
factorTwo: 3
});
multiply(factor: number | ((source: Example) => number)) {
return (source: Observable<Example>) =>
source.pipe(map(value => {
const f = typeof factor === 'function' ? factor(value) : factor;
return value.val * f;
}))
}
// Use with number
simpleUsage$ = this.source$.pipe(this.multiply(1))
// Use with function
WithFactorOne$ = this.source$.pipe(this.multiply(x => x.factor))
WithFactorTwo$ = this.source$.pipe(this.multiply(x => x.factorTwo))
Added note:
If you were to put a return type on the multiply function above, it would read:
function multiply(factor: number | ((source: Example) => number)): OperatorFunction<Example, number>
Since map
is an operator function itself, you don't need to manually construct your own by defining a function over a source observable, you can just return the map
Like this:
function multiply(
factor: number | ((source: Example) => number)
): OperatorFunction<Example, number> {
return map(value => {
const f = typeof factor === 'function' ? factor(value) : factor;
return value.val * f;
});
}
CodePudding user response:
multiply
shouldn't concern itself with streams at all, just do the multiplication. It could look like this:
type FactorDefinition = {
factorValue?: number,
factorName?: 'factor' | 'factorTwo',
};
const multiply = (
inputValue,
{ factorName, factorValue }: FactorDefinition,
) => {
const multiplier = factorName ? inputValue[factorName] : factorValue;
return inputValue.val * multiplier;
};
const simpleUsage$ = source$.pipe(
map((value) => multiply(value, { factorValue: 1 }))
);
withFactorOne$ = source$.pipe(
map((value) => multiply(value, { factorName: 'factor' }))
);
https://stackblitz.com/edit/rxjs-2un7xf?devtoolsheight=60&file=index.ts