SelectMany
1. 概要
SelectMany は、上流から流れてくる各値に対して Observable を生成し、それらすべてを フラットに合流 させて下流へ流すオペレーターです。
他言語では flatMap や mergeMap と呼ばれる操作に相当します。上流の 1 つの値から複数の値を展開したい場合や、値をもとに別の Observable を購読してその結果を合流させたい場合に使います。
2. シグネチャ
値から Observable を生成して合流する
csharp
public static Observable<TResult> SelectMany<TSource, TResult>(
this Observable<TSource> source,
Func<TSource, Observable<TResult>> selector)各値に selector を適用して Observable を生成し、すべての内部 Observable の値をフラットに合流させます。
csharp
source.SelectMany(x => Observable.Range(0, x))値から Observable を生成し、結果を変換して合流する
csharp
public static Observable<TResult> SelectMany<TSource, TCollection, TResult>(
this Observable<TSource> source,
Func<TSource, Observable<TCollection>> collectionSelector,
Func<TSource, TCollection, TResult> resultSelector)collectionSelector で内部 Observable を生成し、各内部値と元の上流値を resultSelector で組み合わせて変換します。
csharp
source.SelectMany(
x => Observable.Range(0, x),
(original, inner) => $"{original} -> {inner}")インデックス付きで Observable を生成して合流する
csharp
public static Observable<TResult> SelectMany<TSource, TResult>(
this Observable<TSource> source,
Func<TSource, int, Observable<TResult>> selector)selector の第 2 引数に 0 始まりのインデックスが渡されます。
csharp
source.SelectMany((x, i) => Observable.Return($"[{i}] {x}"))インデックス付きで Observable を生成し、結果を変換して合流する
csharp
public static Observable<TResult> SelectMany<TSource, TCollection, TResult>(
this Observable<TSource> source,
Func<TSource, int, Observable<TCollection>> collectionSelector,
Func<TSource, int, TCollection, int, TResult> resultSelector)collectionSelector と resultSelector の両方にインデックスが渡される overload です。resultSelector には、上流のインデックスと内部 Observable のインデックスの両方が渡されます。
csharp
source.SelectMany(
(x, si) => Observable.Range(0, x),
(src, si, col, ci) => $"src[{si}]={src}, col[{ci}]={col}")overload の使い分け
| overload | 使う場面 |
|---|---|
SelectMany(Func<TSource, Observable<TResult>>) | 各値から Observable を展開して合流する基本形 |
SelectMany(Func<TSource, Observable<TCollection>>, Func<..., TResult>) | 元の値と内部値を組み合わせた変換が必要な場合 |
SelectMany(Func<TSource, int, Observable<TResult>>) | インデックスを利用したい場合 |
SelectMany(Func<..., int, ...>, Func<..., int, ..., int, TResult>) | インデックスと結果変換の両方が必要な場合 |
3. マーブルダイアグラム
上流の各値から内部 Observable が生成され、それらの値がフラットに合流して下流へ流れます。内部 Observable が並行して動作するため、出力順は各内部 Observable の発行タイミングに依存します。
4. サンプルコード
csharp
using R3;
var source = Observable.Create<int>(observer =>
{
observer.OnNext(1);
observer.OnNext(2);
observer.OnNext(3);
observer.OnCompleted();
return Disposable.Empty;
});
// 各値から Range を展開して合流
source.SelectMany(x => Observable.Range(1, x))
.Subscribe(x => Console.WriteLine(x));
// 出力: 1, 1, 2, 1, 2, 3元の値と内部値を組み合わせる例:
csharp
using R3;
var source = Observable.Range(1, 3);
source.SelectMany(
x => Observable.Range(1, x),
(original, inner) => $"{original} * {inner} = {original * inner}")
.Subscribe(x => Console.WriteLine(x));
// 出力:
// 1 * 1 = 1
// 2 * 1 = 2
// 2 * 2 = 4
// 3 * 1 = 3
// 3 * 2 = 6
// 3 * 3 = 95. 補足
- 単純な 1 対 1 の同期変換には Select を使用してください。
SelectManyは 1 対多の展開やネストされた Observable のフラット化に使います。 - 非同期処理で 1 対 1 の変換を行いたい場合は SelectAwait のほうが適切です。