web-dev-qa-db-ja.com

Observable.fromから放出された値の配列を収集するにはどうすればよいですか?

Rxjsにはたくさんのコードがあり、

return Observable.from(input_array)
           .concatMap((item)=>{
               //this part emits an Observable.of<string> for each item in the input_array
           })
           .scan((output_array:string[],each_item_output_array:string)=>{
               return output_array.Push(each_item_output_array) ;
           });

しかし、明らかにこれは間違っています。スキャンによってconcatMap内のコードが壊れるので、監視可能なfrom演算子で各項目の出力配列を収集する方法を知りたいですか?

9
tomriddle_1234

scan の呼び出しで、アキュムレータのシードを指定していません。その場合、最初の値がシードとして使用されます。例えば:

Rx.Observable
  .from(["a", "b", "c"])
  .scan((acc, value) => acc + value)
  .subscribe(value => console.log(value));
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>

スニペットでは、最初の値は配列ではないため、Pushを呼び出すことはできません。値を配列に累積するには、次のように配列シードを指定できます。

Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .scan((acc, value) => {
    acc.Push(value);
    return acc;
  }, []) // Note that an empty array is use as the seed
  .subscribe(value => console.log(JSON.stringify(value)));
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>

ただし、一部のユースケースでは、配列を変更しない方が望ましい場合があります。

Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .scan((acc, value) => [...acc, value], [])
  .subscribe(value => console.log(JSON.stringify(value)));
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>

scanは、受け取る値ごとに配列を生成することに注意してください。オブザーバブルが完了したときに単一の配列のみを出力したい場合は、代わりにtoArray演算子を使用できます。

Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .toArray()
  .subscribe(value => console.log(JSON.stringify(value)));
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
31
cartant

別のオプションはbufferCount(count)です。入力配列の長さがわかっている場合、その数の項目を含む単一の出力を取得できます。 scanの使用方法を覚えておかなければならないよりも、構文が簡潔です。

注:サイズがわからない場合(例ではわかっていますが)、countは最大値を表しますが、これにはリソースの制約がある可能性があるため、99999にしないでください。

_ const array = source$.pipe(bufferCount(10));
_

私の場合、実行中の「操作」のリストがあり、ステップの総数が事前にわかっていたので、bufferCountは非常にうまく機能しました。ただし、エラー条件を慎重に検討してください。

_ // one approach to handling errors that still returns an output array
 // only use this if a single failure shouldn't stop the collection
 const array = source$.pipe(
                    map((result) => ({ hasError: false, result: result }),
                    catchError((err) => ({ hasError: true, error: err }),
                    bufferCount(10));
_

エラーの処理方法の決定は、実際のオブザーバブルが何であるかによって大きく異なりますが、ここでのポイントは、bufferCount()をオプションとして表示することです。

(利用可能な他のbuffer操作もあります)

1
Simon_Weaver

このコードに注意してください:

      const obs = Rx.Observable
      .from(["a", "b", "c"])
      .concatMap(value => Rx.Observable.of(value))
      .scan((acc, value) => {
        acc.Push(value);
        return acc;
      }, []); 
      obs.subscribe(value => console.log(JSON.stringify(value)));
      obs.subscribe(value => console.log(JSON.stringify(value)));

結果は少し予想外になります:

 ["a"]
 ["a","b"]
 ["a","b","c"]
 ["a","b","c","a"]
 ["a","b","c","a","b"]
 ["a","b","c","a","b","c"]

「acc」変数は参照オブジェクトであり、各サブスクライバーはストリームデータを取得し、同じオブジェクトにデータを再度追加します。これを回避するための多くの解決策になる可能性があります。これは、ストリームデータが再度受信されたときに新しいオブジェクトを作成することです。

    var obs = Rx.Observable
          .from(["a", "b", "c"])
          .concatMap(value => Rx.Observable.of(value))
          .scan((acc, value) => {
          //clone initial value
            if (acc.length == 0) {

                   acc = [];
                }
            acc.Push(value);
            return acc 
          }, []); // Note that an empty array is use as the seed
          obs.subscribe(value => console.log(JSON.stringify(value)));
          obs.subscribe(value => console.log(JSON.stringify(value)));

期待どおりの結果:

 ["a"]
 ["a","b"]
 ["a","b","c"]
 ["a"]
 ["a","b"]
 ["a","b","c"]

私はそれが誰かのために多くの時間を節約することを望みます

0
Oleg Bondarenko