skip to Main Content

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?

2

Answers


  1. 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

    Login or Signup to reply.
  2. I think your approach is a valid one and to construct a dynamic pipe I’d use a reducer to build the pipe from transformer simply by piping the observables upon each other.

    Stackblitz (re-used demo by @NarenMurali, thx).

    stringTransformers
      .reduce(
        (obs, transformer) =>
          obs.pipe(concatMap((value) => transformer.transform(value))),
        of([initialStr])
      )
      .subscribe((output) => console.log('final', output));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search