RXJS — динамическое использование concatMap

Я пытаюсь выполнить асинхронные манипуляции со строками в массиве. Класс имеет массив объектов, у которых есть метод преобразования, возвращающий наблюдаемую величину. Каждый вызов преобразования зависит от результата предыдущего.

Моей первой мыслью было использовать concatMap, но поскольку наблюдаемые должны быть созданы с выводом предыдущего, я не верю, что смогу использовать этот оператор.

Используя concatMap, это будет выглядеть примерно так:

        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
            ));

Как я могу воссоздать шаблон concatMap выше, но динамически масштабировать его в соответствии с размером массива?

🤔 А знаете ли вы, что...
JavaScript можно использовать для создания видеоигр, как 2D, так и 3D, с использованием библиотеки Three.js.


2
64
2

Ответы:

Решено

Это моя версия решения. Я использую expand для рекурсивного вызова наблюдаемого массива до тех пор, пока не останется элементов, затем я использую last для вывода окончательного значения.

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.info('final', output));

Демо-версия Stackblitz


Я думаю, что ваш подход верен, и для создания динамического канала я бы использовал редуктор для создания канала из трансформатора, просто перенаправляя наблюдаемые друг на друга.

Stackblitz (повторное использование демо от @NarenMurali, спасибо).

stringTransformers
  .reduce(
    (obs, transformer) =>
      obs.pipe(concatMap((value) => transformer.transform(value))),
    of([initialStr])
  )
  .subscribe((output) => console.info('final', output));