前回に引き続き、変換演算子についてです。
今回は、ToLookupメソッドです。
ToLookupメソッドは、ToDictionaryメソッドと似ていて
どちらも、キーと値の組み合わせを持ちます。
違いは、一対多のマッピングデータを構築出来る点です。
ToDictionaryメソッドの一対多版のような感じです。
個人的によく利用します。
ToLookupメソッドの書式は、以下のようになっています。
public static ILookup<TKey, TSource> ToLookup<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector )
public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector )
書式的には、ToDictionaryメソッドと同じです。
戻り値で返ってくるオブジェクトが異なっています。
戻り値である、ILookupは初めて見た時には混乱するかもしれません。
以下のようなインターフェース定義となっています。
public interface ILookup<TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>, IEnumerable { ... }
ややこしいですね・・・
要は、ILookup自体が、IGroupingのリストのような感じです。
IGroupingは、クエリ式でgroup byを行った際に利用されているものです。
てことで、ILookupを処理する場合は、通常以下のように2重でループを
行います。
ILookup<int, xxx> aLookup = query.ToLookup(item => item.Id); // 各グループ取得 foreach (var group in aLookup) { // キー Console.WriteLine(group.Key); // グループのデータを列挙. foreach (var data in group) { Console.WriteLine(data); } }
他にも、ILookup自体にインデクサが定義されているので
キー値が分かっている場合は、以下のようにもできます。
if (aLookup.Contains(100)) { foreach (var data in aLookup[100]) { Console.WriteLine(data); } }
以下、サンプルです。
#region LinqSamples-15 public class LinqSamples15 : IExecutable { class Person { public int Id { get; set; } public string Name { get; set; } public string Team { get; set; } } public void Execute() { var persons = new List<Person> { new Person{ Id = 1, Name = "gsf_zero1", Team = "TeamA" } ,new Person{ Id = 2, Name = "gsf_zero2", Team = "TeamB" } ,new Person{ Id = 3, Name = "gsf_zero3", Team = "TeamA" } ,new Person{ Id = 4, Name = "gsf_zero4", Team = "TeamA" } ,new Person{ Id = 5, Name = "gsf_zero5", Team = "TeamB" } ,new Person{ Id = 6, Name = "gsf_zero6", Team = "TeamC" } }; var query = from aPerson in persons select aPerson; // // Teamの値をキーとして、グルーピング処理. // これにより、各キー毎に合致するPersonオブジェクトが紐付いた構造に変換される。 // // 実際のオブジェクトの型は // Lookup<Grouping<string, Person>> // となっている。 // // 尚、Lookupオブジェクトを外部から新規で構築することはできない。 // // 以下では、keySelectorを指定している。 ILookup<string, Person> lookup = query.ToLookup(aPerson => aPerson.Team); // // ILookupに定義されているプロパティにアクセス. // 通常、Lookupオブジェクトはループを経由してデータを取得するが、キーを指定して、アクセスすることもできる。 // Console.WriteLine("カウント={0}", lookup.Count); foreach (Person teamAPerson in lookup["TeamA"]) { Console.WriteLine(teamAPerson); } // // ILookup<TKey, TElement>は、IEnumerable<IGrouping<TKey, TElement>>を継承しているので // ループさせることで、IGrouping<TKey, TElement>を取得することができる。 // // このIGrouping<TKey, TElement>が一対多のマッピングを実現している。 // 尚、IGrouping<TKey, TElement>は、クエリ式にてgroup byを行った際にも取得できる。 // Console.WriteLine("=========== ToLookupに対してkeySelectorを指定した版 ============="); foreach (IGrouping<string, Person> grouping in lookup) { Console.WriteLine("KEY={0}", grouping.Key); foreach (var aPerson in grouping) { Console.WriteLine("\tID={0}, NAME={1}", aPerson.Id, aPerson.Name); } } // // keySelectorとelementSelectorを指定してみる。 // var lookup2 = query.ToLookup(aPerson => aPerson.Team, aPerson => string.Format("{0}_{1}", aPerson.Id, aPerson.Name)); Console.WriteLine("=========== ToLookupに対してkeySelectorとelementSelectorを指定した版 ============="); foreach (var grouping in lookup2) { Console.WriteLine("KEY={0}", grouping.Key); foreach (var element in grouping) { Console.WriteLine("\t{0}", element); } } } } #endregion