web-dev-qa-db-ja.com

Observableを取得して変換されたアイテムの配列を返す方法(Rxjs)

製品のjsonリストを生成するエンドポイントがあります。 Productsのコードでカスタムクラスタイプを定義しています。エンドポイントからデータを取得し、製品のjson配列をProductクラスの配列に変換しようとしています。

サンプルAPI json(私の実際のデータから簡略化):

{
    "products": [{
        "id": 1,
        "name": "Product 1",
        "materials": [{
            "id": 100,
            "desc": "wood"
        }, {
            "id": 101,
            "desc": "metal"
        }]
    }, {
        "id": 2,
        "name": "Product 2",
        "materials": [{
            "id": 100,
            "desc": "wood"
        }, {
            "id": 102,
            "desc": "brick"
        }]
    }]
}

マイコード:

loadProducts(){
    this.fetchProducts().subscribe(data => {
            console.log("the data:", data);
        })
}


fetchProducts(): Observable<Product[]> {
    return this.http.get("http://example.com/mydata.json")
        .map((response) => {
            const products = (response.json()).products;
            console.log("Converting to json" + products.length);
            return products;
        })
        .map( data => {
            console.log("Working on data: ", data);
            return new Product(data.id, data.name, data.materials);
        });

コンソールに表示されるのは、.

"Converting to json 2"
"Working on data: " [object]
"Working on data: " [object]
"the data:" [object,object]

..しかし、私が見ているのは..

"Converting to json 2"
"Working on data: " [object,object]
"the data:" [object,object]

送信されたアイテムごとに.map関数が実行されると思いました。最初に.mapを呼び出したときに、1つのアイテム(応答)に対して1回実行されていることがわかります。また、2つのアイテムが返されていることがわかります。次に、2番目のマップ関数が2回実行されることを期待します-各製品アイテムに対して1回。代わりに、製品の配列で渡されると呼び出されるようです。

さらに複雑なことに、マテリアルのリストを、作成したマテリアルクラスタイプに変換したいと思います。 forEachループでallを実行できることはわかっていますが、これを「React」の方法で実行したいと考えています。

10
captain_jim1

最終的に、私が探していたものを生み出すために、Observableオペレーションの正しい組み合わせを見つけました。

fetchProducts(): Observable<Product[]> {
    return this.http.get("http://examples.com/myData")
        .map((response) => {
            return response.json().products;
        })
        .switchMap( productArray => {
            return Observable.from(productArray);
        })
        .map( (productData: any) => {
            return new Product(
                productData.id,
                productData.name,
                productData.materials
            );
        })
        .toArray();
}

Observable.mapがどのように機能するかを誤解しました-データの各部分で実際に実行されると、データの各項目で実行されると考えていました。私を助けてくれた@jonrsharpeに感謝します。

SwitchMapを使用して配列から新しいObservableを返すことで、配列内の各データを個別に出力することができました。その助けをありがとう@ giora-guttsait。

最後に、これらの新しいストリームのピースをすべて1つの配列に結合する必要がありました。 Observable.toArray()がこれを行いました。

17
captain_jim1

あなたの場合、あなたはこのようなものを得ています:

_this.http.get(...)
  .map(res => res.json().products)  // turns this into Observable<Products[]>
  .map(products => ... ) // here, products is already an array.
_

この配列をReactと同じように処理したい場合、つまり各製品に対して何かを行う場合は、次のようにします。

_this.http.get(...)
  .map(res => res.json().products)
  .switchMap(products => Observable.from(products))
_

ここで、Observable.from(products)は_Observable<Product>_を返し、switchMapは以前に取得した_Observable<Product>_をチェーンに返します。

_this.http.get(...)
  .map(res => res.json().products)
  .switchMap(products => Observable.from(products))
  .subscribe(product => console.log(product))
_

それらの製品をすべて印刷します。

Disclamer:このコードを実行してチェックしていません。switchMapとは異なる演算子が必要な場合がありますが、これは機能することを覚えています

1
Giora Guttsait