2017-02-27 30 views
8

Mój serwer często zwraca dane w postaci tablicy wewnątrz obiektu RxJS 5 Observable (używam Angular 2).Najlepszy sposób na "spłaszczyć" tablicę wewnątrz obiektu RxJS Observable

I często znajduję się chcąc procesie elementy tablicy indywidualnie z RxJS operatorów i robię to z następującego kodu (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(); 

Linia mergeMap(val => val) nie czuje się bardzo idiomatyczne.

Czy istnieje lepszy sposób zastosowania transformacji do elementów tablicy, która jest emitowana przez obserwowalne?

NB. Chcę, aby operatory RxJS (w przeciwieństwie do metod tablicowych) przekształcały moje elementy, ponieważ potrzebuję możliwości rzutowania każdego elementu na drugi obserwowalny. Typowy przypadek użycia: backend zwraca listę identyfikatorów pozycji i muszę zażądać wszystkich tych elementów z zaplecza.

Odpowiedz

6

Możesz użyć concatAll() lub mergeAll() bez żadnego parametru.

dataFromBackend 
    .do(items => console.log(items)) 
    .mergeAll() // or .concatAll() 

poniżej (w tym mergeMap) działa tylko RxJS 5, ponieważ traktuje wykrywalności tablice obiekty tablicowej Promises itp ten sam sposób.

Ostatecznie można zrobić także:

.mergeMap(val => Observable.from(val) 
    .do(item => console.log(item)) 
    .map(item => item.name) 
) 
.toArray() 
+0

Dzięki, Martin. Czy to prawda, że ​​'concatAll()' zachowałby kolejność elementów w oryginalnej tablicy, a 'mergeAll()' nie zrobiłby tego? – AngularChef

+2

@AngularFrance Ponieważ rozpakowujesz tablicę, nie ma znaczenia, którego z nich użyjesz, a obie będą produkować zamówione przedmioty. Jest to istotne tylko wtedy, gdy jest używane z operacją asynchroniczną, co nie jest w tym przypadku. – martin

1

Jeśli jest synchroniczny, proponuję użyć javascript na Array.map zamiast tego nawet powinny zaoszczędzić trochę wydajność:

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

To jest to, często zdarza mi się rzutować każdy przedmiot na drugi obserwowalny. Typowy przypadek użycia: backend zwraca listę identyfikatorów pozycji i muszę zażądać wszystkich tych elementów z zaplecza. Powinienem uwzględnić to w moim pytaniu. – AngularChef

+0

To ma wpływ na sposób budowania strumienia - ale w tym przypadku prawdopodobnie lepiej jest z odpowiedzią @ martina - chociaż muszę tu napisać osobisty (nieco niepowiązany) komentarz: Zasadniczo jest to zła architektura REST aby najpierw zażądać listy identyfikatorów, a następnie pobrać swoje dane osobno w pojedynczych połączeniach - to tylko tortury dla dowolnego serwera i zwykle trwa znacznie dłużej niż po prostu pobieranie wszystkich danych w jednym pierwszym wywołaniu. – olsn

+0

Pewnie. Ale jak wiesz, czasami jesteśmy tylko * konsumentami * API, a nie jego * twórcami *. Niedawno stanąłem przed przypadkiem użycia, który właśnie opisałem (osobne wywoływanie listy kluczy, a następnie samych podmiotów) za pomocą interfejsu API JavaScript w Gmailu. – AngularChef

0

Właściwie jeśli potrzebujesz go w strumieniu, użyj tego:

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