問題説明
LINQにおけるToDictionaryメソッドでは、キーが重複するとArgumentExceptionが発生します。
sample1.cs
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
using Idol = KeyValuePair<string, int>;
class Program
{
static void Main(string[] args) {
// サンプルデータ
var list = new [] {
new Idol("島村卯月", 17),
new Idol("櫻井桃華", 12),
new Idol("相原雪乃", 22),
new Idol("安部菜々", 17), //ウーサミン!
};
// 例外がスローされました: 'System.ArgumentException' (mscorlib.dll の中)
// 型 'System.ArgumentException' のハンドルされていない例外が mscorlib.dll で発生しました
// 同一のキーを含む項目が既に追加されています。
var dic = list.ToDictionary(idol => idol.Value, idol => idol.Key);
}
}
}
つまり、全部LINQで片付けようとすると、「事前にキーが重複しないか調べる」という面倒な手間が発生するわけです。
もちろん次のように書けば問題ありませんが、やはり冗長に見えますよね?
sample2.cs
using System.Collections.Generic;
namespace ConsoleApp1
{
using Idol = KeyValuePair<string, int>;
class Program
{
static void Main(string[] args) {
// サンプルデータ
var list = new [] {
new Idol("島村卯月", 17),
new Idol("櫻井桃華", 12),
new Idol("相原雪乃", 22),
new Idol("安部菜々", 17), //ウーサミン!
};
// 無難な追加方法
// dicの中身はKeyValuePair<int, string>型で
// [0] {[17, 安部菜々]}
// [1] {[12, 櫻井桃華]}
// [2] {[22, 相原雪乃]}
var dic = new Dictionary<int, string>();
foreach(var idol in list) {
dic[idol.Value] = idol.Key;
}
}
}
}
解決策
GroupByメソッドを使えば良いのでした。これでキー毎にグルーピングした後、それぞれについてLastメソッドを使えば、上記のsample2.cs
と同じ結果が返ってきます。
sample3.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
using Idol = KeyValuePair<string, int>;
class Program
{
static void Main(string[] args) {
// サンプルデータ
var list = new [] {
new Idol("島村卯月", 17),
new Idol("櫻井桃華", 12),
new Idol("相原雪乃", 22),
new Idol("安部菜々", 17), //ウーサミン!
};
// あくまでLINQによる解決策
// dicの中身はKeyValuePair<int, string>型で
// [0] {[17, 安部菜々]}
// [1] {[12, 櫻井桃華]}
// [2] {[22, 相原雪乃]}
var dic = list.GroupBy(idol => idol.Value)
.ToDictionary(idolGroup => idolGroup.Last(), idolGroup => idolGroup.Key);
}
}
}