javascripttypescriptrxjsrxjs-observables

RXJS - Using concatMap dynamically


I am attempting to do in order async manipulation of strings in an array. The class has an array of objects that have a transform method that return an observable. Each transform call depends on the output of the prior.

My first thought was to use concatMap but as the observables need to be created with the output of the one prior I don't believe I can use that operator.

Using concatMap this would look something like so:

        return of([initialStr])
            .pipe(
                concatMap(strArr => this.stringTransformers[0].transform(strArr)),
                concatMap(strArr => this.stringTransformers[1].transform(strArr)),
                concatMap(strArr => this.stringTransformers[2].transform(strArr)),
                ... until the end of the stringTransformers array
            ));

How can I recreate the concatMap pattern above but dynamically to scale with the size of the array?


Solution

  • This is my version of the solution. I use expand to recursively call the observable array until there are no elements left, then I use last to output the final value.

    import { concatMap } from 'rxjs/operators';
    import './style.css';
    
    import { rx, of, map, expand, EMPTY, last } from 'rxjs';
    
    const initialStr = 'hello';
    
    const stringTransformers = [
      {
        transform: (inputs: Array<string>) =>
          of(inputs.map((input: string) => `${input}-1`)),
      },
      {
        transform: (inputs: Array<string>) =>
          of(inputs.map((input: string) => `${input}-2`)),
      },
      {
        transform: (inputs: Array<string>) =>
          of(inputs.map((input: string) => `${input}-3`)),
      },
      {
        transform: (inputs: Array<string>) =>
          of(inputs.map((input: string) => `${input}-4`)),
      },
      {
        transform: (inputs: Array<string>) =>
          of(inputs.map((input: string) => `${input}-5`)),
      },
    ];
    
    of([initialStr])
      .pipe(
        expand((value: Array<string>, index: number) => {
          return stringTransformers[index]
            ? stringTransformers[index].transform(value)
            : EMPTY;
        }),
        last()
      )
      .subscribe((output) => console.log('final', output));
    

    Stackblitz Demo