ToLookupAsync
1. 概要
ToLookupAsync は、Observable シーケンスのすべての要素を収集し、ILookup<TKey, T> または ILookup<TKey, TElement> を Task として返すオペレーターです。ToDictionaryAsync と異なり、同じキーに対して複数の値をグルーピングできます。シーケンスが完了するまで要素をバッファリングします。
2. シグネチャ
キーセレクターのみ
csharp
public static Task<ILookup<TKey, T>> ToLookupAsync<T, TKey>(
this Observable<T> source,
Func<T, TKey> keySelector,
CancellationToken cancellationToken = default)各要素からキーを抽出し、同じキーを持つ要素をグループ化します。
キーセレクターと要素セレクター
csharp
public static Task<ILookup<TKey, TElement>> ToLookupAsync<T, TKey, TElement>(
this Observable<T> source,
Func<T, TKey> keySelector,
Func<T, TElement> elementSelector,
CancellationToken cancellationToken = default)各要素からキーと値の両方を変換してルックアップに格納します。
キーセレクターとカスタム等値比較子
csharp
public static Task<ILookup<TKey, T>> ToLookupAsync<T, TKey>(
this Observable<T> source,
Func<T, TKey> keySelector,
IEqualityComparer<TKey> comparer,
CancellationToken cancellationToken = default)キーの比較にカスタムの IEqualityComparer<TKey> を使用します。
キーセレクター、要素セレクター、カスタム等値比較子
csharp
public static Task<ILookup<TKey, TElement>> ToLookupAsync<T, TKey, TElement>(
this Observable<T> source,
Func<T, TKey> keySelector,
Func<T, TElement> elementSelector,
IEqualityComparer<TKey> comparer,
CancellationToken cancellationToken = default)キーと値の変換、およびカスタムのキー比較ロジックをすべて指定します。
オーバーロードの使い分け
| オーバーロード | 用途 |
|---|---|
ToLookupAsync(source, keySelector) | 要素自体を値として格納する場合 |
ToLookupAsync(source, keySelector, elementSelector) | 値も変換したい場合 |
ToLookupAsync(source, keySelector, comparer) | カスタムキー比較が必要な場合 |
ToLookupAsync(source, keySelector, elementSelector, comparer) | 値の変換とカスタムキー比較の両方が必要な場合 |
3. マーブルダイアグラム
シーケンスのすべての要素をキーごとにグループ化して収集し、完了時にルックアップを単一の結果として出力します。
4. サンプルコード
csharp
// 基本的な使い方:カテゴリごとにグループ化
var products = new[]
{
new { Category = "果物", Name = "りんご" },
new { Category = "野菜", Name = "にんじん" },
new { Category = "果物", Name = "バナナ" },
new { Category = "野菜", Name = "キャベツ" },
new { Category = "果物", Name = "みかん" },
};
var lookup = await products.ToObservable()
.ToLookupAsync(p => p.Category);
foreach (var item in lookup["果物"])
{
Console.WriteLine(item.Name);
}
// りんご
// バナナ
// みかんcsharp
// 要素セレクターを使用して名前だけを格納
var products = new[]
{
new { Category = "果物", Name = "りんご" },
new { Category = "野菜", Name = "にんじん" },
new { Category = "果物", Name = "バナナ" },
};
ILookup<string, string> lookup = await products.ToObservable()
.ToLookupAsync(p => p.Category, p => p.Name);
Console.WriteLine(string.Join(", ", lookup["果物"]));
// りんご, バナナcsharp
// 存在しないキーへのアクセスは空のシーケンスを返す(例外なし)
var numbers = new[] { 1, 2, 3, 4, 5 }.ToObservable();
ILookup<string, int> lookup = await numbers
.ToLookupAsync(n => n % 2 == 0 ? "偶数" : "奇数");
Console.WriteLine(lookup["偶数"].Count()); // 2
Console.WriteLine(lookup["存在しないキー"].Count()); // 0