angular - 保存する追加データを含む Observable の RxJS 配列

okwaves2024-01-25  8

簡単な説明

パイプ&マップで使用する必要がある各 Observable のデータを渡して、Observable の配列を (forkJoin などで) 処理するにはどうすればよいですか?

const source = {animal: 'cat', fruit: 'apple', color: 'blue'}
const observables = Object.keys(source).map(key => [this.getDataFromApi(source[key]), key])

// resolve only observables[0][0], observables[0][1], observables[0][2] in magic way,
// but preserve  observables[1][0], observables[1][1], observables[1][2] for future use in pipe&map
const processedObservable = forkJoin(observables).pipe( // ???
  map(items => items.map(item => 'this is ' + item[0] + '(' + item[1] + ')')), // this doesn't work
  map(items => items.join(', '))
)

processedObservable.subscribe(text => console.log(text)) // subscribe only for test
// expected result: this is your cat (animal), this is your apple (fruit), this is your blue (color)
長い説明

いくつかの「ソース」があります。 (項目の配列またはオブジェクト)。 API にすべての項目をリクエストする必要があるため、Observable の配列を取得します。 次に、受信したデータをすべて処理したいので、forkJoin を使用して、パイプといくつかのマップ内のデータを処理します。

サブスクライブでデータを直接処理できません。

簡単な例を次に示します。

const source = ['cat', 'apple', 'blue']
const observables = source.map(item => this.getDataFromApi(item))
const processedObservable = forkJoin(observables).pipe(
  map(items => items.map(item => 'this is ' + item)),
  map(items => items.join(', '))
)
processedObservable.subscribe(text => console.log(text)) // test
// result: this is your cat, this is your apple, this is your blue

しかし、API リクエストのアイテムのデータの他に、パイプとパイプでの処理中に使用する必要があるアイテムのメタデータがあります。地図。

ここでは代表的なソースを使用した例を示しますが、ここではアイテムのメタデータを使用しません (結果は上記と同じです)。私無視されたメタデータ:

const source = {animal: 'cat', fruit: 'apple', color: 'blue'}
const observables = Object.keys(source).map(key => this.getDataFromApi(source[key]))
const processedObservable = forkJoin(observables).pipe(
  map(items => items.map(item => 'this is ' + item)),
  map(items => items.join(', '))
)
processedObservable.subscribe(text => console.log(text)) // test
// result: this is your cat, this is your apple, this is your blue

これは代表的なソースの例ですが、ここではキーと API 呼び出しを無視していますが、アイテムのメタデータを処理しています。

const source = {animal: 'cat', fruit: 'apple', color: 'blue'}
const observables = Object.keys(source).map(key => of(key))
const processedObservable = forkJoin(observables).pipe(
  map(items => items.map(item => '(' + item + ')')),
  map(items => items.join(', '))
)
processedObservable.subscribe(text => console.log(text)) // test
// result: (animal), (fruit), (color)

この結果を取得したい:

// result: this is your cat (animal), this is your apple (fruit), this is your blue (color)

pipe&map では次のようになります:

  map(items => items.map(item => 'this is ' + item.apiValue + '(' + item.key + ')')),

または:

  map(items => items.map(item => 'this is ' + item[0] + '(' + item[1] + ')')),

しかし、オブザーバブルとメタデータの配列を forkJoin に渡す方法がわかりません。このオブザーバブルの配列の一部にはメタデータが含まれています。

const observables = Object.keys(source).map(key => [this.getDataFromApi(source[key]), key])

フラットマップやスイッチマップなど、別の関数を使用したほうがよいでしょうか?

追加情報

API 呼び出しをシミュレートするメソッド getDataFromApi:

  getDataFromApi(item) {
    return of('your ' + item)
  }


------------------------

最初のマップを直接追加する必要があります。個々のオブザーバブルを作成する前に。

const source = { animal: "cat", fruit: "apple", color: "blue" };

const observables = Object.keys(source).map(key => getFromApi(source[key]).pipe(
  map(item => `this is ${item} (${key})`)
));

const processedObservable = forkJoin(observables).pipe(
  map(items => items.join(', '))
);

processedObservable.subscribe(console.log);

https://stackblitz.com/edit/rxjs-4x7hbv?file=index.ts

0



------------------------

以下は実装方法の例です。

from(Object.entries(source))
 .pipe(
   mergeMap(([key, value]) => getFromApi(value).pipe(
     map(item => `this is ${item} (${key})`), // <= key is being closured
   )),
   toArray(), // <= transform stream to array 
   map(item => item.join()), 

 )
 .subscribe(console.log);

次のデモを実行してみてください。

const { from, of } = rxjs;
const { map, switchMap, toArray } = rxjs.operators;

function getFromApi(item) {
  return of('your ' + item)
}

const source = { animal: "cat", fruit: "apple", color: "blue" };


from(Object.entries(source))
  .pipe(
    mergeMap(([key, value]) => getFromApi(value).pipe(
      map(item => `this is ${item} (${key})`), // <= key is being closured
    )),
    toArray(), // <= transform stream to array 
    map(item => item.join()),
  )
  .subscribe(console.log);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.6.2/rxjs.umd.min.js"></script>

2

これは、非同期 API 呼び出しでは機能しません。スイスitchMap は、API 呼び出しをサブスクライブ解除し、前の呼び出しが完了する前に次の API 呼び出しをサブスクライブします。この動作は、('y ' + item).pipe(delay(0)) のシミュレートされた API 呼び出しに遅延を追加すると発生します。

– フリド

2020 年 9 月 8 日 9:53

mergeMap は、マージされた Observable がソースからのアイテムが到着したのと同じ順序で API 応答を出力することを保証しません。最初の API 呼び出しが 2 番目の API 呼び出しよりも時間がかかる場合、マージされたオブザーバブルは最初の応答が発行される前に 2 番目の応答を発行します。したがって、配列内の項目の順序は cotoArray によって選択される項目は、ソース配列内の項目の順序と異なる場合があります。

– フリド

2020 年 9 月 8 日 14:54



------------------------

function getDataFromApi(item) {
  return of('your ' + item)
}

const source = { animal: 'cat', fruit: 'apple', color: 'blue' };

from(Object.keys(source)).pipe(
  switchMap(key => forkJoin([getDataFromApi(source[key]), of(key)])),
  map(([data, key]) => `this is ${data} (${key})`),
  toArray(),
  map(data => data.join(', ')),
).subscribe(console.log);

参照: https://stackblitz.com/edit/rxjs-1t4nlo?file=index.ts

1

これは、非同期 API 呼び出しでは機能しません。 switchMap は API 呼び出しをサブスクライブ解除し、前の API 呼び出しの前に次の API 呼び出しをサブスクライブします。完了しました。この動作は、('y ' + item).pipe(delay(0)) のシミュレートされた API 呼び出しに遅延を追加すると発生します。

– フリド

2020 年 9 月 8 日 9:53



------------------------

「rxjs」から import {combineLarge } を使用して以下を試してください

 combineLatest(['cat', 'apple', 'blue'].map(item => this.getDataFromApi(item))).pipe(
    map(items => items.map(item => 'this is ' + item)),
    map(items => items.join(', '))
  )
  .subscribe({
    next:(res) => console.log(res)
  })

stackblitz の例

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。