Jose R. Chacón
Jose R. Chacón

Reputation: 101

Repeat query until condition is met

I do an HTTP request to an API that returns 350 records. Each item have a property called "total". I need to select 5 items randomly but also, the sum of the property "total" of those 5 items should be less than 30. I think this can be done using takeUntil, but I still don't know how to build the code in order to accomplish that.

products$ = combineLatest([
    this._productService.robots$,
    this.refreshProductAction$
  ]).pipe(
    map(([products, action]) =>
      products.sort(() => (Math.random() - Math.random())).slice(0, 5),
    )
  );

The is one example of "expand" operator, but i don't know how to keep track of the sum and the re execute the observable again in case is higher than 50.

Upvotes: 0

Views: 621

Answers (1)

Picci
Picci

Reputation: 17752

If I understand right, you receive 350 "products" with just one API invocation which needs to be repeated if the sum of "total" of 5 randomly chosen "products" is higher than 50.

So, if this understanding is correct, I would proceed using expand operator to recursively invoke the API until the desired condition is met and then use the last operator to notify only the last value emitted, which is the first one satisfying the condition. The following example tries to explain the logic with inline comments.

products$ = combineLatest([
  this._productService.robots$,
  this.refreshProductAction$
]).pipe(
  // use expand operator to recursively repeat the API call if the sum is higher than 50, 
  // otherwise return the empty Observable, which means simply to complete the stream
  expand(([products, action]) => {
    const fiveProducts = products.sort(() => (Math.random() - Math.random())).slice(0, 5);
    // use the "reduce" method of Array to calculate the sum
    const sumOfFiveTotals = fiveProducts.reduce((s, p) => s = s + p.total, 0);
    return sumOfFiveTotals > 50 ? 
       // I assume this._productService.robots$, when subscribed, calls the API
       // you can pipe a "delay" operator after this._productService.robots$ if you want to add some delay between subsequent API calls
       this._productService.robots$ :
       EMPTY  // the empty Observable
  }),
  // notifies only the last value emitted by the upstream
  last()
);

Upvotes: 1

Related Questions