ILookup<TKey, TVal>
と IGrouping<TKey, TVal>
の違いを明確にするのに苦労してきましたが、今正しく理解できているかどうか興味があります。 LINQは、IGrouping
アイテムのシーケンスを生成すると同時に、ToLookup
拡張メソッドを提供することで、問題を悪化させました。ですから、よく見るまでは同じように感じました。
var q1 =
from n in N
group n by n.MyKey into g
select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>
これは次と同等です:
var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>
これはよく似ています:
var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>
私は次の例えで正しいですか?
IGrouping<TKey, TVal>
は単一のグループ(つまり、キー付きシーケンス)であり、値が実際には(単一の要素ではなく)要素のシーケンスであるKeyValuePair<TKey, TVal>
に類似しています。IEnumerable<IGrouping<TKey, TVal>>
は、それらのシーケンスです(IDictionary<TKey, TVal>
を反復処理したときに得られるものと同様です。ILookup<TKey, TVal>
はIDictionary<TKey, TVal>
に似ており、値は実際には要素のシーケンスです。はい、それらはすべて正しいです。
また、ILookup<TKey, TValue>
はIEnumerable<IGrouping<TKey, TValue>>
も拡張するため、特定のキーを検索するだけでなく、すべてのキー/コレクションのペアを反復処理できます。
私は基本的にILookup<TKey,TValue>
をIDictionary<TKey, IEnumerable<TValue>>
のようなものだと思っています。
ToLookup
は「今すぐ実行」操作(即時実行)であるのに対し、GroupBy
は延期されることに注意してください。たまたま、「pull LINQ」の動作方法では、IGrouping
の結果からGroupBy
sをプルし始めると、とにかくすべてのデータを読み取る必要があります(できないため)途中でグループを切り替えます)一方、他の実装では、ストリーミング結果を生成できる場合があります。 (プッシュLINQで実行されます。LINQtoEventsは同じであると思います。)
ILookupとIDictionaryには、もう1つの重要な違いがあります。前者は、データを変更する方法がないという意味で不変性を強制します(コンシューマーが明示的なキャストを実行する場合を除く)。対照的に、IDictionaryには、データの変更を可能にする「追加」のようなメソッドがあります。したがって、関数型プログラミングや並列プログラミングの観点からは、ILookupの方が優れています。 (グループではなく、キーに1つの値のみを割り当てるバージョンのILookupもあればいいのにと思います。)
(ところで、IEnumerableとIListの関係は、ILookupとIDictionaryの関係にいくぶん似ていることを指摘する価値があるようです。前者は不変ですが、後者は不変です。)
GroupBy
とToLookUp
の機能はほぼ同じです[〜#〜]ただし[〜#〜]これ:- 参照
GroupBy:GroupBy演算子は、いくつかのキー値に基づいて要素のグループを返します。各グループは、IGroupingオブジェクトによって表されます。
ToLookup:ToLookupはGroupByと同じです。唯一の違いは、GroupByの実行が延期されるのに対し、ToLookupの実行は即時であるということです。
サンプルコードを使用して違いを明確にしましょう。 Person
モデルを表すクラスがあるとします。
class Personnel
{
public int Id { get; set; }
public string FullName { get; set; }
public int Level { get; set; }
}
その後、personnels
のリストを次のように定義します。
var personnels = new List<Personnel>
{
new Personnel { Id = 1, FullName = "P1", Level = 1 },
new Personnel { Id = 2, FullName = "P2", Level = 2 },
new Personnel { Id = 3, FullName = "P3", Level = 1 },
new Personnel { Id = 4, FullName = "P4", Level = 1 },
new Personnel { Id = 5, FullName = "P5", Level =2 },
new Personnel { Id = 6, FullName = "P6", Level = 2 },
new Personnel { Id = 7, FullName = "P7", Level = 2 }
};
次に、personnels
をレベルごとにグループ化する必要があります。ここでは2つのアプローチがあります。 GroupBy
またはToLookUp
を使用します。前に述べたように、GroupBy
を使用すると、遅延実行が使用されます。つまり、コレクションを反復処理すると、次のアイテムが呼び出されるまで計算されない場合があります。
var groups = personnels.GroupBy(p => p.Level);
personnels.RemoveAll(p => p.Level == 1);
foreach (var product in groups)
{
Console.WriteLine(product.Key);
foreach (var item in product)
Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
}
上記のコードでは、最初にpersonnels
をグループ化しましたが、それを繰り返す前に、いくつかのpersonnels
を削除しました。 GroupBy
は遅延実行を使用するため、グループ化はここのforeach
ポイントで計算されるため、最終結果には削除されたアイテムは含まれません。
出力:
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2
しかし、上記のコードを以下のように書き直すと、(コードは前のコードと同じですが、GroupBy
がToLookUp
に置き換えられることに注意してください)
var groups = personnels.ToLookup(p => p.Level);
personnels.RemoveAll(p => p.Level == 1);
foreach (var product in groups)
{
Console.WriteLine(product.Key);
foreach (var item in product)
Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
}
ToLookUp
は即時実行を使用するため、ToLookUp
メソッドを呼び出すと、結果が生成され、グループが適用されます。したがって、反復前にpersonnels
からアイテムを削除すると、それは最終結果に影響を与えません。
出力:
1
1 >>> P1 >>> 1
3 >>> P3 >>> 1
4 >>> P4 >>> 1
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2
注:GroupBy
とToLookUp
はどちらも異なるタイプを返します。
ToLookUpの代わりにToDictionaryを使用することもできますが、これに注意する必要があります:( 参照 )
ToLookup()の使用法は、ToDictionary()の使用法と非常に似ており、どちらもキーセレクター、値セレクター、および比較子を指定できます。主な違いは、ToLookup()は重複キーを許可(および期待)するのに対し、ToDictionary()は許可しないことです。