Glen
Glen

Reputation: 331

sum in list of observale in angular

This is my code

//Service method

 private cartObs$: BehaviorSubject<CartItem[]> = new BehaviorSubject(null);

   getCartItems(): Observable<CartItem[]> {
        return this.cartObs$.asObservable();
      }

//method in component

sum$: Observable<number>;

this.sum$ = this.cartService
          .getCartItems()
          .pipe(map((cart) =>
           cart.reduce((total, item) => total + item.qty, 0)));

i need to calculate sum of qty in rxjs. am getting error if obeservable is empty how to calculate sum in rxjs with empty list

I am following similar to this arrayof obervable sum

Thanks in advance

ERROR

ERROR TypeError: Cannot read property 'reduce' of null
    at MapSubscriber.eval [as project] (https://cddm0ddd.ddd.app/src/app/components/header/header.component.ts:17:50)

    at MapSubscriber._next (https://cugm0.csb.app/node_modules/rxjs/_esm5/internal/operators/map.js:47:29)

    at MapSubscriber.Subscriber.next (https://cugm0.csb.app/node_modules/rxjs/_esm5/internal/Subscriber.js:73:12)

    at BehaviorSubject._subscribe (https://cugm0.csb.app/node_modules/rxjs/_esm5/internal/BehaviorSubject.js:34:18)

    at BehaviorSubje

Upvotes: 0

Views: 524

Answers (2)

Barremian
Barremian

Reputation: 31125

Although the solution @VovaBilyachat solves the issue, there are some edge cases where it might exhibit erroneous behavior.

Consider the following

1. Default value empty array

In this case the BehaviorSubject is initialized with an empty array as default value.

private cartObs$: BehaviorSubject<CartItem[]> = new BehaviorSubject([]);

In this case you'd get the same error as the question if some component would push null or undefined to it like cartObs$.next(null).

2. Checking if the value is defined inside map

While this would the issue from solution 1, it still returns the sum as 0 while the input array is either null or undefined. This might not be expected from the usage PoV of the sum$ observable.

To tackle these you could use the filter operator in the sum$ observable to use the cartObs$ emissions only when they are defined.

this.sum$ = this.cartService.getCartItems().pipe(
  filter(cart => (!!cart && !!(cart.length))),
  map((cart: any) => cart.reduce((total, item) => total + item.qty, 0))
);

That said, if the BehaviorSubject's default value isn't used anywhere, I'd recommend you to use ReplaySubject with buffer 1 instead. It'll also "hold" the current value and emit it to future subscribers without the need for a default value.

private cartObs$: ReplaySubject<CartItem[]> = new ReplaySubject(1);

Upvotes: 1

Vova Bilyachat
Vova Bilyachat

Reputation: 19474

I would assume that your this.cartObs$ is of type BehaviorSubject and by default it require to set default value. So I think you are setting default value to null instead of an empty array or you are emmiting null to your subject.

So I see two options here:

Set to empty array private cartObs$: BehaviorSubject<CartItem[]> = new BehaviorSubject([]);

Second fix reducer so it can handle null values ( I think this is safest way since then you dont care if someone will push null into your subject)

this.sum$ = this.cartService
          .getCartItems()
          .pipe(map((cart) => cart ? 
           cart.reduce((total, item) => total + item.qty, 0)) : 0);

cart ? cart.reduce((total, item) => total + item.qty, 0)) : 0 this means if cart is not null run reduce of its null return 0

Upvotes: 1

Related Questions