ListからDictionary作る時もLINQを使おうぜ！ILookupも便利だぜ！

# はじめに

　みなさんLINQ使っていますか？LINQ最高ですよね！

　さて、ListからDictionary作るようなことをしませんか？空のDictionaryを作って、foreach文を使ってListをまわし、Dictionaryに要素を追加していってDictionaryを作るコードなどを書きませんか？

　実はLINQを使って非常に簡潔に、ListなどのクラスからDictionaryを作ることができるのです。


# Dictionaryを作る時、もしかしたらこんなコード書きません？

　こんな列挙型とクラスがあります。

```csharp:Element列挙型とSkillクラス
public enum Element
{
	Fire,
	Thunder,
	Wind,
}

public class Skill
{
	public int Id { get; set; }
	public string Name { get; set; }
	public Element Element { get; set; }
}
```

　List\<Skill\>型のインスタンス、skillListがあります。ちなみにint型のプロパティIdの値はそれぞれのSkillで固有で、重複はしません。

```csharp:
List<Skill> skillList = new List<Skill> {
	new Skill {
		Id = 0,
		Name = "ファイアー",
		Element = Element.Fire,
	},
	new Skill {
		Id = 1,
		Name = "エルファイアー",
		Element = Element.Fire,
	},
	new Skill {
		Id = 2,
		Name = "サンダー",
		Element = Element.Thunder,
	},
	new Skill {
		Id = 3,
		Name = "サンダーストーム",
		Element = Element.Thunder,
	},
	new Skill {
		Id = 4,
		Name = "エイルカリバー",
		Element = Element.Wind,
	}
};
```

　さてさて、このList\<Skill\>のskillListから、int型のIdをキーとし、SkillをバリューとするDictionary\<int, Skill\>を作ります。次のようなコードです。

```csharp:ListからforeachでDictionaryを作る
Dictionary<int, Skill> skillDictonary = new Dictionary<int, Skill> ();
foreach (Skill skill in skillList) {
	skillDictionary.Add (skill.Id, skill);
}
```

　使うとしたらこんなかんじでしょうか。

```csharp:作ったDictionaryを使う
int skillId = GetTargetSkillId (); // 対象のId(int型)を取得
Skill targetSkill = skillDictionary [skillId];
```

　どうでしょう、このようなListからDictionaryを作るコードを書いたことはありませんか？実は、このようなコードはLINQを使えば1行で書けてしまうのです。

# LINQのToDictionaryメソッドを使って1発でDictionaryを作成！

　LINQのToDictionaryメソッドを使って、さきほどのコードを書き換えます。


```csharp:ToDictionaryメソッドを使って、ListからDictionarを生成
Dictionary<int, Skill> skillDictonary = skillList.ToDictionary (skill => skill.Id);
```

　非常に簡潔ですね。1行で書けてしまいました！これだけのコード量で、目的のDictionaryができてしまいました。

　ToDictionaryメソッドはLINQのメソッドで、List\<T\>型でなくても、IEnumeralble\<T\>型を実装したクラスのインスタンスであれば利用できます。ToDictionaryメソッドは他のLINQのメソッドと同様に、[複数のオーバーロード](http://msdn.microsoft.com/ja-jp/library/system.linq.enumerable.todictionary(v=vs.100).aspx)を持っています。

　ここで使った[オーバーロード](http://msdn.microsoft.com/ja-jp/library/bb549277(v=vs.100).aspx)は、引数にデリゲートをとります。IEnumerable\<T\>の各要素がDictionaryのバリューになります。引数に渡したデリゲートを、各要素(バリュー)に適用した結果が要素(バリュー)に対応するキーとなります。


　MSDNに記載されている構文を次に示します。

```csharp:ToDictionaryの構文1
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
	this IEnumerable<TSource> source,
	Func<TSource, TKey> keySelector
)
```

　もし、二つの要素に対して同じキーが生成された場合、ArgumentExceptionが発生します。

# こんなDictionaryもできます！

　さきほどはSkillのIdからSkillを引くDictonary\<int, Skill\>を作りました。次のようなIdから名前を引けるDictionary\<int, string\>を作ることもあるのではないでしょうか？

```csharp:ListからforeachでDictionaryを作る(その2)
Dictionary<int, string> skillNameDictonary = new Dictionary<int, string> ();
foreach (Skill skill in skillList) {
	skillDictionary.Add (skill.Id, skill.Name);
}
```

　使い方は次のような感じでしょうか。

```csharp:
int skillId = GetSkillId ();  // 対象のId(int型)を取得
string skillName = skillDictonary [skillId];
```

　このようなDictionaryもLINQを使えば、1行で作れてしまいます。

```csharp:ToDictionaryメソッドを使って、ListからDictionaryを生成
Dictionary<int, string> skillDictonary = skillList.ToDictionary (skill => skill.Id, skill => skill.Name);
```

　ここで使った[オーバーロード](http://msdn.microsoft.com/ja-jp/library/bb548657(v=vs.100).aspx)は、引数に二つのデリゲートをとります。イメージとしてはIEnumerable\<T\>のぞれぞれ各要素に対して、1つ目のデリゲートを適用した結果をキーとし、2つ目のデリゲートを適用した結果をバリューとしたキーとバリューのペアを作り、それらからDictionaryが作られるイメージです。

MSDNの構文は次の通りです。

```csharp:ToDictionaryの構文2
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
	this IEnumerable<TSource> source,
	Func<TSource, TKey> keySelector,
	Func<TSource, TElement> elementSelector
)
```

# えっ？Dictionary\<TKey, List\<TValue\>\>が欲しいって？

　前の二つのDictionaryはどちらも、int型のIdをキーとしていました。Idは重複がないものでしたので、Idに対応するSkillが一意に決まりましたね。

　もしElement型のキーからSkillを引きたい場合どうすればいいでしょうか？あるElementに対応するSkillは複数あり、Elementから一意のSkillに決定はできないのでDictionary\<Element, Skill\>は使えませんね。そこで次のような、Dictionary\<Element, List\<Skill\>\>型を作ることが考えられますね。

```csharp:
Dictionary<Element, List<Skill>> elementSkillListDictonary = new Dictionary<Element, List<Skill>>();
foreach (Skill skill in skillList) {
	if (elementSkillListDictonary.ContainsKey (skill.Element)) {
		elementSkillListDictonary[skill.Element].Add (skill);
	} else {
		elementSkillListDictonary[skill.Element] = new List<Skill> {skill}; 
	}
}
```

　長いですね．．．次のようにElementがFireのものを指定して引いたり、全要素をキーバリューペアごとにまわし、Elementのグループごとに処理をしたりもする使い方が考えられます。

```csharp:
List<Skill> fireSkillNameList = elementSkillListDictonary[Element.Fire];
foreach (Skill fireSkill in fireSkillNameList) {
	Debug.Log (fireSkill.Name);
}
```

```csharp:
foreach (KeyValuePair<Element, List<Skill>> item in elementSkillListDictonary) {
	Debug.Log (item.Key);
	List<Skill> skills = item.Value;
	foreach (Skill skill in skills) {
		Debug.Log (skill.Name);
	}
}
```

　使いどころはありそうですが、いかんせんバリューがListのDictionaryを作成するコードが長いですね。LINQで簡潔にかけいないのでしょうか。実はこの処理もLINQを使って1行で...残念ながらこれとまったく同じことをするLINQメソッドはありません。(まぁ複数のLINQメソッドを組み合わせることで、できなくはないのですが、それよりもいい方法があります。)

　上記とまったっく同じものを作るのではないのですが、LINQのToLookupというメソッドを使えばやりたいことを実現できると思います。

# ToLookupメソッド、使ってみて！

　それでは、[ToLookup](http://msdn.microsoft.com/ja-jp/library/system.linq.enumerable.tolookup(v=vs.100).aspx)メソッドを説明します。下記のコードは[ToLookup](http://msdn.microsoft.com/ja-jp/library/bb549073(v=vs.100).aspx)メソッドのサンプルです。

```csharp:ToLookupの例
ILookup<Element, Skill> elementSkillLookup = skillList.ToLookup (skill => skill.Element);
```

　これだけ見ても何ができるか分かりませんね。ToLookupメソッドの返り値型、ILookupインターフェースのインスタンスelementSkillLookupで、さきほどDictionaryでやったことと同じことをやってみましょう。

```csharp:
IEnumerable<Skill> fireSkills = elementSkillLookup[Element.Fire];
foreach (Skill fireSkill in fireSkills) {
	Debug.Log (fireSkill.Name);
}
```

　List\<Skill\>型ではなく、IEnumerable\<Skill\>型ですが、Dictionaryのようにインデクサーが使えます。 

```csharp:
foreach (IGrouping<Element, Skill> group in elementSkillLookup) {
	Debug.Log (group.Key);
	IEnumerable<Skill> skills = group;
	foreach (Skill skill in skills) { // groupでforeachすることもできる
		Debug.Log (skill.Name);
	}
}
```

　ILookup\<TKey, TLement\>インターフェースは、IEnumerable\<IGrouping\<TKey, TElement\>\>を継承しています。次のようにIGrouping\<Element, Skill\>ごとにforeachでまわすことができます。先のDictionaryのようにグループごとの処理も可能です。

　バリューがListのDictionaryでやったことが、ILookupでもできますね。

　さて、ToLookupメソッドも[いくつかのオーバーロード](http://msdn.microsoft.com/ja-jp/library/system.linq.enumerable.tolookup(v=vs.100).aspx)があります。ToDictionaryと同じように二つのデリゲートを引数にとるものがあります。

```csharp:
ILookup<Element, string> elementSkillNameLookup = skillList.ToLookup (
	skill => skill.Element,
	skill => skill.Name
);

IEnumerable<string> fireSkillNames = elementSkillNameLookup[Element.Fire];
foreach (string fireSkillName in fireSkillNames) {
	Debug.Log (fireSkillName);
}

foreach (IGrouping<Element, string> group in elementSkillNameLookup) {
	Debug.Log (group.Key);
	IEnumerable<string> skillNames = group;
	foreach (string skillName in skillNames) {
		Debug.Log (skillName);
	}
}
```

ちなみにToLookupとToDictionaryを用いて、バリューにListをもつDictionaryをつくることも可能です。

```csharp:
Dictionary<Element, List<Skill>> elementSkillListDictonary = skillList
	.ToLookup (skill => skill.Element)
	.ToDictionary (
		skillGroup => skillGroup.Key,
		skillGroup => skillGroup.ToList ()
	);
```

# まとめ

　WhereやSelectなどIEnumerable\<T\>からIEnumerable\<T\>を作るメソッド、Any、All、Count、Sumなど真理値や数字を作るメソッドは、LINQを紹介する投稿やブログ、サンプルコードでも良く見ますね。

　それらに埋もれがちですが、List\<T\>や配列などからDictionary<TKey, TValue>を作るメソッド、ToDictionaryもあります。利用シーンは前述のメソッドに比べると多くないかもしれません。ですがそれを使えば簡潔に目的のDictionaryを生成できます。


　また、冗長になりがちなDictionary\<TKey, List\<TValue\>\>を生成するコード。ToLookupメソッドを用いて、ILookup<TKey, TElement>を生成し、それで代用することが可能です。あまりブログなどでこのクラスは見かけませんが、ぜひ使ってみてください！