Scan & Reduce operators In Angular

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.

Post a Comment

Previous Post Next Post