web-dev-qa-db-fra.com

Meilleure façon "d'aplatir" un tableau à l'intérieur d'un observable RxJS

Mon backend renvoie souvent des données sous la forme d'un tableau à l'intérieur d'un observable RxJS 5 (j'utilise Angular 2).

Je me retrouve souvent à vouloir traiter les éléments du tableau individuellement avec des opérateurs RxJS et je le fais avec le code suivant ( 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();

La ligne mergeMap(val => val) ne semble pas très idiomatique.

Existe-t-il un meilleur moyen d'appliquer des transformations aux membres d'un tableau émis par un observable?

NB Je veux que les opérateurs RxJS (vs méthodes de tableau) transforment mes éléments, car j'ai besoin de la capacité de projeter chaque élément dans un deuxième observable. Cas d'utilisation typique: retour de la liste des identifiants d'élément dans le backend et demande que tous ces éléments soient demandés à partir du backend.

30
AngularChef

Vous pouvez utiliser concatAll() ou mergeAll() sans paramètre.

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

Ceci (y compris mergeMap) ne fonctionne que dans RxJS 5 car il traite les observables, les tableaux, les objets de type tableau, les promesses, etc. de la même manière.

Finalement, vous pourriez aussi faire:

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

Jan 2019: Mise à jour pour RxJS 6

27
martin

En fait, si vous en avez besoin dans le flux, utilisez ceci:

.flatMap(items => of(...items))
4
Nick Waits

Note angulaire 6.

Si vous utilisez comme opérateur canalisable, do est appelé tap!

https://www.learnrxjs.io/operators/utility/do.html Voici un exemple.

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

S'il s'agit d'une opération synchrone, je suggérerais d'utiliser Array.map à la place, cela devrait même vous faire économiser quelques performances:

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