Best way to “flatten” an array inside an RxJS Observable

你离开我真会死。 提交于 2019-11-27 01:28:55

问题


My backend frequently returns data as an array inside an RxJS 5 Observable (I'm using Angular 2).

I often find myself wanting to process the array items individually with RxJS operators and I do so with the following code (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();

The mergeMap(val => val) line doesn't feel very idiomatic.

Is there a better way to apply transformations to the members of an array that's emitted by an Observable?

NB. I want RxJS operators (vs array methods) to transform my items because I need the ability to project each item into a second observable. Typical use case: backend returns of list of item ids and I need to request all of these items from the backend.


回答1:


You can use concatAll() or mergeAll() without any parameter.

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

This (including mergeMap) works only in RxJS 5 because it treats Observables, arrays, array-like objects, Promises, etc. the same way.

Eventually you could do also:

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

Jan 2019: Updated for RxJS 6




回答2:


Actually if you need it inside the stream just use this:

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



回答3:


If it is a synchronous operation, I would suggest to use javascript's Array.map instead, it even should save you some performance:

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



回答4:


Angular 6 note.

If you are using as a pipeable operator, do is known as tap!

https://www.learnrxjs.io/operators/utility/do.html Here is an example.

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


来源:https://stackoverflow.com/questions/42482705/best-way-to-flatten-an-array-inside-an-rxjs-observable

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!