Scan & Reduce operators In Angular
In this article, we will learn about the Scan & Reduce operators in Angular. The accumulator function is applied to the values of the source observable by the Scan & Reduce Operators in Angular. Reduce simply emits the last result, but the Scan Operator returns all intermediate results of the accumulation. Both employ a seed value as the beginning value, which is optional.
Scan in Angular
The scan operator emits each value after successively applying an accumulator function to the values generated by the source Observableble.
Syntax
scan<t r="">(accumulator: (acc: R, value: T, index: number) => R, seed?: T | R): OperatorFunction<t r="">
Where
- The function that is called on each source value is known as the accumulator.
- The starting accumulation value is called seed (optional)
Three arguments are passed to the accumulator function.
- The accumulator variable, acc, is where the values are added up.
- The value is derived from the source observable,
- The index of the value.
The seed provides the beginning value for the acc.
The scan operator performs the accumulator function on these two variables and emits the result when the first value arrives from the source.
The result of the previous step becomes the input when the second value arrives from the source ( acc ). The scan produces a new result, which becomes the third emission's input.
This loop will continue till the stream is finished.
Scan Example
The accumulator function is (acc, value) => acc + value in the example below. 0 is the seed.
import { Component } from "@angular/core"; import { map, scan } from "rxjs/operators"; import { interval, of } from "rxjs"; @Component({ selector: "my-app", template: ` <h1>Scan Operator Example</h1> `, styleUrls: ["./app.component.css"] }) export class AppComponent { reTryCount = 0; ngOnInit() { of(1, 2, 3, 4, 5) .pipe(scan((acc, value) => acc + value, 0)) .subscribe( val => console.log(val), e => console.log(e), () => console.log("Complete") ); } } ***Console*** 1 3 6 10 15 Complete
The value for acc starts at zero, which is the seed value. The value of the variable value is set to 1, which is the source's initial value. The scan operator emits the result of the accumulator function (acc + value = 1).
The result of the previous accumulator function is used as the next scan's input (acc). It is multiplied by the next number (i.e. 2) emitted by the source, yielding 3.
This will continue till the sequence is completed.
We adjust the seed to 10 in the following code. Instead of starting with zero, the accumulator now starts with ten.
of(1, 2, 3, 4, 5) .pipe(scan((acc, value) => acc + value, 10)) .subscribe( val => console.log(val), e => console.log(e), () => console.log("Complete") ); *** Result *** 11 13 16 20 25 Complete
Combining as Arrays
of(1, 2, 3, 4, 5) .pipe(scan((acc, value) => [...acc, value], [])) .subscribe( val => console.log(val), e => console.log(e), () => console.log("Complete") ); *** Console *** [1] [1, 2] [1, 2, 3] [1, 2, 3, 4] [1, 2, 3, 4, 5]
Tracking Button Clicks
import { Component, ElementRef, ViewChild } from "@angular/core"; import { map, scan } from "rxjs/operators"; import { fromEvent, interval, of, Subscription } from "rxjs"; @Component({ selector: "my-app", template: ` <h1>Scan Operator Example</h1> <button btn="">Button</button> `, styleUrls: ["./app.component.css"] }) export class AppComponent { @ViewChild("btn", { static: true }) button: ElementRef; sub: Subscription; ngAfterViewInit() { this.sub = fromEvent(this.button.nativeElement, "click") .pipe(scan((acc, value) => acc + 1, 0)) .subscribe(val => console.log("You clicked " + val + " times")); } ngOnDestroy() { this.sub.unsubscribe(); } }
Reduce in Angular
When the source completes, the Reduce operator applies an accumulator function to the values emitted by the source Observation in order and returns the collected result.
Except for the following differences, the Reduce operator works identically to the scan operator.
- The intermediate findings are not returned.
- It only reappears once the source has finished.
Reduce Example
The scan example above is comparable to the following example. The only difference is that the intermediate results, such as 1, 3, 6, and 10, will not be seen.
ngOnInit() { of(1, 2, 3, 4, 5) .pipe(reduce((acc, value) => acc + value, 0)) .subscribe( val => console.log(val), e => console.log(e), () => console.log("Complete") ); } ** Console ** 15 Complete
Combining as Arrays
ngOnInit() { of(1, 2, 3, 4, 5) .pipe(reduce((acc, value) => [...acc, value], [])) .subscribe( val => console.log(val), e => console.log(e), () => console.log("Complete") ); } ** Console *** [1, 2, 3, 4, 5] Complete
Tracking Button Clicks
Only if the observable is complete does the Reduce operator emit. As a result, just replacing the scan with the reduction in the Tracking button click example will not work.
In the following example, we use the Subject to build a new observable and use the event binding to emit the click event. As a result, we can raise the whole notification.
import { Component, ElementRef, ViewChild } from "@angular/core"; import { map, reduce, scan } from "rxjs/operators"; import { fromEvent, interval, of, Subject, Subscription } from "rxjs"; @Component({ selector: "my-app", template: ` <h1>Reduce Operator Example</h1> <button click="" clickme="" event="">Click Me</button> <br /> <br /> <br /> <button click="" event="" startcounting="">Sart</button> <button click="" stopcounting="">Stop</button> `, styleUrls: ["./app.component.css"] }) export class AppComponent { clickStream: Subject<event>; sub: Subscription; ngOnInit() {} clickMe(event: Event) { console.log("Clicked"); if (this.clickStream) this.clickStream.next(event); } startCounting(event: Event) { this.clickStream = new Subject<event>(); this.sub = this.clickStream .asObservable() .pipe(reduce((acc, value) => acc + 1, 0)) .subscribe(val => console.log("You clicked " + val + " times")); } stopCounting() { this.clickStream.complete(); } ngOnDestroy() {} }
Conclusion
In this article, we learned about the Scan & Reduce operators and how to use them in Angular.
I hope this article helps you and you will like it.👍
If you have any doubt or confusion then free to ask in the comment section.