Подтвердить что ты не робот

Лучший способ "сгладить" массив внутри наблюдаемого RxJS

Мой backend часто возвращает данные как массив внутри RxJS 5 Observable (я использую Angular 2).

Я часто обнаруживаю, что хочу обрабатывать элементы массива индивидуально с операторами RxJS, и я делаю это со следующим кодом (JSBin):

const dataFromBackend = Rx.Observable.of([
  { name: 'item1', active: true },
  { name: 'item2', active: false },
  { name: 'item3', active: true }
]);

dataFromBackend
  // At this point, the obs emits a SINGLE array of items
  .do(items => console.log(items))
  // I flatten the array so that the obs emits each item INDIVIDUALLY
  .mergeMap(val => val)
  // At this point, the obs emits each item individually
  .do(item => console.log(item))
  // I can keep transforming each item using RxJS operators.
  // Most likely, I will project the item into another obs with mergeMap()
  .map(item => item.name)
  // When I'm done transforming the items, I gather them in a single array again
  .toArray()
  .subscribe();

Линия mergeMap(val => val) не очень идиоматична.

Есть ли лучший способ применить преобразования к элементам массива, которые испускаются Observable?

NB. Я хочу, чтобы операторы RxJS (vs array methods) меняли мои объекты, потому что мне нужна возможность проецировать каждый элемент во второй наблюдаемый. Типичный вариант использования: backend возвращает список идентификаторов элементов, и мне нужно запросить все эти элементы из бэкэнд.

4b9b3361

Ответ 1

Вы можете использовать concatAll() или mergeAll() без каких-либо параметров.

dataFromBackend.pipe(
  tap(items => console.log(items)),
  mergeAll(), // or concatAll()
)

Это (в том числе mergeMap) работает только в RxJS 5, поскольку обрабатывает Observables, массивы, объекты, похожие на массивы, Promises и т.д. Одинаково.

В конце концов вы могли бы также сделать:

mergeMap(val => from(val).pipe(
  tap(item => console.log(item)),
  map(item => item.name),
)),
toArray(),

Январь 2019: обновлено для RxJS 6

Ответ 2

Собственно, если вам нужно это внутри потока, просто используйте это:

.flatMap(items => of(...items))

Ответ 3

Угловая 6 нота.

Если вы используете в качестве оператора pipeable, do называется tap!

https://www.learnrxjs.io/operators/utility/do.html Вот пример.

// RxJS 
import { tap, map, of, mergeMap} from 'rxjs/operators';

backendSource
  .pipe(
   tap(items => console.log(items)),
   mergeMap(item => item ),
   map(item => console.log(item.property))
  );

Ответ 4

Если это синхронная операция, я бы предложил использовать javascript Array.map вместо этого, это даже должно сэкономить вам некоторую производительность:

const dataFromBackend = Rx.Observable.of([
  { name: 'item1', active: true },
  { name: 'item2', active: false },
  { name: 'item3', active: true }
]);

dataFromBackend
  .map(items => items.map(item => item.name))
  .subscribe();