Dictionaryの拡張メソッド 36選

# 概要
C#のDictionaryは便利なクラスですが、メソッド関係はけっこう貧弱です。
様々なLINQ拡張メソッドが含まれている[Interactive Extensions(Ix.NET)](https://www.nuget.org/packages/System.Interactive/)にもDictionary関係はありません。
Dictionaryに値が含まれているかどうかの条件分岐が絡むことが多いため、
ちょっとした操作で3行ぐらい消費してしまいます。

そこで定型的な処理をまとめた、C#のDictionaryをもっと使いやすくする拡張メソッドを紹介します。




# デモ用コード
各拡張メソッドの説明の前に、デモで使用するコードについて説明します。
ソース配列の生成メソッドと式木を使ってDictionaryを見やすくコンソール出力するメソッドです。
（参考）[式木を使った簡単なPrintデバッグ出力](https://qiita.com/soi/items/fcb80d0b43c81b547a7b)

```csharp
/// <summary>
/// 元Dictionaryの生成
/// </summary>
private static Dictionary<string, int> CreateSourceDictionary()
    => new Dictionary<string, int>
    {
        ["A"] = 10,
        ["B"] = 20,
    };

/// <summary>
/// Dictionaryの内容をコンソール出力 ex.「sourceDict: { A = 10, B = 20, }」
/// </summary>
private static void ConsoleWriteDictionary<TKey, TValue>(Expression<Func<Dictionary<TKey, TValue>>> dictExp)
{
    var memName = (dictExp.Body as MemberExpression)?.Member.Name;
    var dict = dictExp.Compile().Invoke();
    Console.Write($"{memName}: {{ ");
    foreach (var kv in dict)
    {
        Console.Write($"{kv.Key} = {kv.Value}, ");
    }
    Console.WriteLine("}");
}
```

こんな感じで動きます。


```csharp:デモコード
Dictionary<string, int> sourceDict = CreateSourceDictionary();
Console.WriteLine("# Start DictionaryExtension Demo");
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# Start DictionaryExtension Demo
sourceDict: { A = 10, B = 20, }
```


# Keyがないなら～する系

## GetOrDefault
これを書いたことのある人は多いのではないでしょうか。
指定したKeyがDictionaryにあれば、そのValueを返し、ないならValue型のDefault値を返します。


```csharp:実装
public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
    => dict.TryGetValue(key, out TValue result) ? result : default(TValue);
```

```csharp:デモコード
Console.WriteLine($"# {nameof(DictionaryExtension.GetOrDefault)}");
Console.WriteLine($"exist value = {sourceDict.GetOrDefault("A")}");
Console.WriteLine($"unexist value = {sourceDict.GetOrDefault("C")}");
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# GetOrDefault
exist value = 10
unexist value = 0
sourceDict: { A = 10, B = 20, }
```



## TryAdd
指定したKeyがDictionaryにあれば、何もせず、ないなら指定したValueと共に追加します。
追加を実行したかを返します。

```csharp:実装
public static bool TryAddNew<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key) where TValue : new()
    => dict.TryAdd(key, _ => new TValue());

public static bool TryAddDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
    => dict.TryAdd(key, default(TValue));

public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue addValue)
{
    bool canAdd = !dict.ContainsKey(key);

    if (canAdd)
        dict.Add(key, addValue);

    return canAdd;
}

public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> addValueFactory)
{
    bool canAdd = !dict.ContainsKey(key);

    if (canAdd)
        dict.Add(key, addValueFactory(key));

    return canAdd;
}
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.TryAdd)}");

Console.WriteLine($"A result = {sourceDict.TryAdd("A", 1000)}");//これのみ無視される
Console.WriteLine($"C result = {sourceDict.TryAdd("C", 3000)}");
Console.WriteLine($"D result = {sourceDict.TryAdd("D", _ => 4000)}");
Console.WriteLine($"E result = {sourceDict.TryAddNew("E")}");
Console.WriteLine($"F result = {sourceDict.TryAddDefault("F")}");
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# TryAdd
A result = False
C result = True
D result = True
E result = True
F result = True
sourceDict: { A = 10, B = 20, C = 3000, D = 4000, E = 0, F = 0, }
```

ちょっと名前の違う2つのメソッドの違い

- `TryAddNew`はValueが`new TValue()`
- `TryAddDefault`はValueが`default(TValue)`


`TryAdd`の2つのオーバーロードの使い分け

- Valueが参照型や軽量な値型の場合は `TryAdd(..., TValue addValue)`
- Valueを毎回生成したい場合や、生成処理をできるだけしたくないのであれば`TryAdd(..., Func<TKey, TValue> addValueFactory)`

Addやインデックスでの入力との違いは指定したKeyがDictionaryにある時の挙動です。
Addで例外発生するのがいやな時に使うことが多いと思います。

| 種類 | Keyなし| Keyあり|
|:-:|:-:|:-:|
| [key] = value  | keyとvalueの追加  | valueの上書き  |
| Add(key, value)  |  keyとvalueの追加 | 例外発生 |
| TryAdd(key, value) | keyとvalueの追加 | 変化なし  |

## GetOrAdd
命名がイマイチ気に入ってない。。。
指定したKeyがDictionaryにあれば、そのValueを返し、ないなら値を追加した上で返します。
キャッシュとしてDictionaryを使うときなどに有用です。
TryAddと値の取得を組み合わせたものです。

```csharp:実装
public static TValue GetOrAddNew<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key) where TValue : new()
{
    dict.TryAddNew(key);
    return dict[key];
}

public static TValue GetOrAddDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
{
    dict.TryAddDefault(key);
    return dict[key];
}

public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue addValue)
{
    dict.TryAdd(key, addValue);
    return dict[key];
}

public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> valueFactory)
{
    dict.TryAdd(key, valueFactory);
    return dict[key];
}
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.GetOrAdd)}");
Console.WriteLine($"exist value = {sourceDict.GetOrAdd("A", 10000)}");//これのみDictionaryが変化しない
Console.WriteLine($"unexist value = {sourceDict.GetOrAdd("C", 30000)}");
Console.WriteLine($"unexist value = {sourceDict.GetOrAdd("D", _ => 40000)}");
Console.WriteLine($"unexist value = {sourceDict.GetOrAddNew("E")}");
Console.WriteLine($"unexist value = {sourceDict.GetOrAddDefault("F")}");
ConsoleWriteDictionary(() => sourceDict);
```



```console:デモ実行結果
# GetOrAdd
exist value = 10
unexist value = 30000
unexist value = 40000
unexist value = 0
unexist value = 0
sourceDict: { A = 10, B = 20, C = 30000, D = 40000, E = 0, F = 0, }
```

# Dictionary加工系

## Add(KeyValuePair)
1つのKeyValuePairを追加します。
あるDictionaryの要素をLINQなどで加工した後に、KeyValuePairのまま、別のDictionaryに追加したい時などに使用します。

```csharp:実装
public static void Add<TKey, TValue>(this IDictionary<TKey, TValue> source, KeyValuePair<TKey, TValue> addPair)
    => source.Add(addPair.Key, addPair.Value);
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.Add)}(KeyValuePair)");
sourceDict.Add(new KeyValuePair<string, int>("C", 300));
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# Add(KeyValuePair)
sourceDict: { A = 10, B = 20, C = 300, }
```








## AddOrUpdate
指定したKeyがDictionaryにあれば、指定したデリゲートでValueを更新します、ないなら指定したValueと共にKeyを追加します。
ConcurrentDictionaryの同名のメソッドをまねたものです。

```csharp:実装
public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
    => dict[key] = dict.TryGetValue(key, out TValue result) ? updateValueFactory(key, result) : addValue;

public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue addValue, Func<TValue, TValue> updateValueFactory)
    => dict[key] = dict.TryGetValue(key, out TValue result) ? updateValueFactory(result) : addValue;

public static TValue AddDefaultOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue, TValue> updateValueFactory)
    => dict.AddOrUpdate(key, default(TValue), updateValueFactory);

public static TValue AddDefaultOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TValue, TValue> updateValueFactory)
    => dict.AddOrUpdate(key, default(TValue), updateValueFactory);

public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
    => dict[key] = dict.TryGetValue(key, out TValue result) ? updateValueFactory(key, result) : addValueFactory(key);

public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TValue> addValueFactory, Func<TValue, TValue> updateValueFactory)
    => dict[key] = dict.TryGetValue(key, out TValue result) ? updateValueFactory(result) : addValueFactory();
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.AddOrUpdate)}");
Console.WriteLine($"A result = {sourceDict.AddOrUpdate("A", 1, (_, oldValue) => oldValue * 10)}");
Console.WriteLine($"B result = {sourceDict.AddOrUpdate("B", 2, (_, oldValue) => oldValue * 10)}");
Console.WriteLine($"B result = {sourceDict.AddOrUpdate("B", 2, (oldValue) => oldValue * 10)}");
Console.WriteLine($"C result = {sourceDict.AddOrUpdate("C", 3, (oldValue) => oldValue * 10)}");
Console.WriteLine($"D result = {sourceDict.AddDefaultOrUpdate("D", (oldValue) => oldValue + 40)}");
Console.WriteLine($"D result = {sourceDict.AddDefaultOrUpdate("D", (oldValue) => oldValue + 40)}");
Console.WriteLine($"E result = {sourceDict.AddOrUpdate("E", _ => 5, (_, oldValue) => oldValue * 10)}");
Console.WriteLine($"E result = {sourceDict.AddOrUpdate("E", () => 5, (oldValue) => oldValue * 10)}");
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# AddOrUpdate
A result = 100
B result = 200
B result = 2000
C result = 3
D result = 0
D result = 40
E result = 5
E result = 50
sourceDict: { A = 100, B = 2000, C = 3, D = 40, E = 50, }
```



## AddRange
複数のKeyValuePairを追加します。
DicitionaryをLINQで加工した後などをまるごと追加したい場合に使用します。

```csharp:実装
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> source, IEnumerable<KeyValuePair<TKey, TValue>> addPairs)
{
    foreach (var kv in addPairs)
    {
        source.Add(kv);
    }
}
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.AddRange)}");
var secondDict = new Dictionary<string, int>
{
    ["C"] = 300,
    ["D"] = 400,
};
sourceDict.AddRange(secondDict);
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# AddRange
sourceDict: { A = 10, B = 20, C = 300, D = 400, }
```





## RemoveByValue
DictionaryのRemoveメソッドはKeyを指定して要素を削除します。
このメソッドはValueを指定して一致する要素をすべて削除します。

```csharp:実装
public static void RemoveByValue<TKey, TValue>(this IDictionary<TKey, TValue> source, TValue value)
{
    var removeKeys = source
        .Where(x => EqualityComparer<TValue>.Default.Equals(x.Value, value))
        .Select(x => x.Key)
        .ToArray();

    foreach (var key in removeKeys)
    {
        source.Remove(key);
    }
}
```


```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.RemoveByValue)}");
sourceDict.RemoveByValue(10);
ConsoleWriteDictionary(() => sourceDict);
```


```console:デモ実行結果
# RemoveByValue
sourceDict: { B = 20, }
```

## TryRemove
ConcurrentDictionaryにある同名のメソッドをまねたものです。
指定したKeyを削除しようと試みます。削除に成功した場合はout 引数に削除前の値が入ります。

```csharp:実装
public static bool TryRemove<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, out TValue value)
{
    source.TryGetValue(key, out value);
    return source.Remove(key);
}
```


```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.TryRemove)}");
var successRemoveA = sourceDict.TryRemove("A", out int valueA);
var successRemoveC = sourceDict.TryRemove("C", out int valueC);
Console.WriteLine($"Remove A {successRemoveA} value = {valueA}");
Console.WriteLine($"Remove C {successRemoveC} value = {valueC}");
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# TryRemove
Remove A True value = 10
Remove C False value = 0
sourceDict: { B = 20, }
```




## ReplaceKey
Valueはそのままで、Keyだけ差し替えます。

```csharp:実装
public static void ReplaceKey<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey previousKey, TKey newKey)
{
    var value = source[previousKey];
    source.Remove(previousKey);
    source.Add(newKey, value);
}
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.ReplaceKey)}");
sourceDict.ReplaceKey("A", "AAA");
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# ReplaceKey
sourceDict: { AAA = 10, B = 20, }
```








# KeyValuePair変換系

## KeyValuePair生成
keyとValueを指定してKeyValuePairを生成する方法と
TupleとValueTupleから変換するメソッドです。
直接 `new KeyValuePair<...>(...`とするよりも型名を書く量が減ります。

```csharp:実装
public static KeyValuePair<TKey, TValue> CreateKeyValuePair<TKey, TValue>(TKey key, TValue value)
    => new KeyValuePair<TKey, TValue>(key, value);

public static KeyValuePair<TKey, TValue> ToKeyValuePair<TKey, TValue>(this Tuple<TKey, TValue> tuple)
    => CreateKeyValuePair(tuple.Item1, tuple.Item2);

public static KeyValuePair<TKey, TValue> ToKeyValuePair<TKey, TValue>(this (TKey key, TValue value) tuple)
    => CreateKeyValuePair(tuple.key, tuple.value);
```


```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.CreateKeyValuePair)}, Tuple {nameof(DictionaryExtension.ToKeyValuePair)}");
sourceDict.AddRange(new KeyValuePair<string, int>[]
{
    DictionaryExtension.CreateKeyValuePair("C", 300),
    ("D", 400).ToKeyValuePair(),
    Tuple.Create("E",500).ToKeyValuePair(),
});
ConsoleWriteDictionary(() => sourceDict);
```

```console:デモ実行結果
# CreateKeyValuePair, Tuple ToKeyValuePair
sourceDict: { A = 10, B = 20, C = 300, D = 400, E = 500, }
```




## ToTuple
こんどは逆方向のKeyValuePairからTupleとValueTupleに変換します。

```csharp:実装
public static Tuple<TKey, TValue> ToTuple<TKey, TValue>(this KeyValuePair<TKey, TValue> kv)
    => new Tuple<TKey, TValue>(kv.Key, kv.Value);

public static (TKey key, TValue value) ToValueTuple<TKey, TValue>(this KeyValuePair<TKey, TValue> kv)
    => (kv.Key, kv.Value);
```



```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# KeyValuePair {nameof(DictionaryExtension.ToTuple)}");
Console.WriteLine($"Tuple = {sourceDict.First().ToTuple()}");
Console.WriteLine($"ValueTuple = {sourceDict.First().ToValueTuple()}");
```

```console:デモ実行結果
# KeyValuePair ToTuple
Tuple = (A, 10)
ValueTuple = (A, 10)
```



# Dictionary変換系

## ToDictionary
Dictionaryを複製（ディープコピー）したい時やLINQで加工した後に再度Dictionaryにしたい時に使います。

```csharp:実装
public static Dictionary<Tkey, TValue> ToDictionary<Tkey, TValue>(this IEnumerable<KeyValuePair<Tkey, TValue>> source)
    => source.ToDictionary(
        keySelector: kv => kv.Key,
        elementSelector: kv => kv.Value);
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# ToDictionary (Copy)");
//Dictionaryを別のDictionaryに変換（値コピー）
var copiedDict = sourceDict.ToDictionary();
//元のDictionaryを変更しても、コピー先には反映されない
copiedDict["A"] = 100;
//LINQで加工したものもDictionaryにできる
var filterdDict = sourceDict.Where(x => x.Key == "A").ToDictionary();

Console.WriteLine("(after copy and change )");
ConsoleWriteDictionary(() => copiedDict);
ConsoleWriteDictionary(() => sourceDict);
ConsoleWriteDictionary(() => filterdDict);
```

```console:デモ実行結果
# ToDictionary (Copy)
(after copy and change )
copiedDict: { A = 100, B = 20, }
sourceDict: { A = 10, B = 20, }
filterdDict: { A = 10, }
```


## SelectValue

Keyはそのままで、ValueのみSelectで加工したい時に使用します。
通常のSelectと同じでIndex付きのオーバーロードもあります。
最後に前述のToDictionary()メソッドを使うことで直接Dictionaryに戻せます。

```csharp:実装
public static IEnumerable<KeyValuePair<Tkey, TValueResult>> SelectValue<Tkey, TValueSource, TValueResult>(this IEnumerable<KeyValuePair<Tkey, TValueSource>> source, Func<TValueSource, TValueResult> valueSelector)
    => source.Select(kv => new KeyValuePair<Tkey, TValueResult>(kv.Key, valueSelector(kv.Value)));

public static IEnumerable<KeyValuePair<Tkey, TValueResult>> SelectValue<Tkey, TValueSource, TValueResult>(this IEnumerable<KeyValuePair<Tkey, TValueSource>> source, Func<TValueSource, int, TValueResult> valueSelector)
    => source.Select((kv, index) => new KeyValuePair<Tkey, TValueResult>(kv.Key, valueSelector(kv.Value, index)));
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.SelectValue)}");
var valueChangedDict = sourceDict.SelectValue((x, i) => x * 0.01 + i).ToDictionary();
ConsoleWriteDictionary(() => valueChangedDict);
```

```console:デモ実行結果
# SelectValue
convertedDict: { A = 0.1, B = 1.2, }
```


## SelectKey
SelectValueのKey版です。

```csharp:実装
public static IEnumerable<KeyValuePair<TKeyResult, TValue>> SelectKey<TKeySource, TValue, TKeyResult>(
    this IEnumerable<KeyValuePair<TKeySource, TValue>> source,
    Func<TKeySource, TKeyResult> keySelector)
    => source.Select(kv => new KeyValuePair<TKeyResult, TValue>(keySelector(kv.Key), kv.Value));

public static IEnumerable<KeyValuePair<TKeyResult, TValue>> SelectKey<TKeySource, TValue, TKeyResult>(
    this IEnumerable<KeyValuePair<TKeySource, TValue>> source,
    Func<TKeySource, int, TKeyResult> keySelector)
    => source.Select((kv, index) => new KeyValuePair<TKeyResult, TValue>(keySelector(kv.Key, index), kv.Value));
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.SelectKey)}");
var keyChangedDict = sourceDict.SelectKey((x, i) => $"{x}-{i}").ToDictionary();
ConsoleWriteDictionary(() => keyChangedDict);
```

```console:デモ実行結果
# SelectKey
keyChangedDict: { A-0 = 10, B-1 = 20, }
```


## FlipKeyValue
KeyとValueを入れ替えたDictionaryを返します。
デモにもあるように、Valueが重複していると例外が発生します。

```csharp:実装
public static Dictionary<TValueToKey, TkeyToValue> FlipKeyValue<TkeyToValue, TValueToKey>(this IEnumerable<KeyValuePair<TkeyToValue, TValueToKey>> source)
    => source.ToDictionary(
        keySelector: kv => kv.Value,
        elementSelector: kv => kv.Key);
```

```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.FlipKeyValue)}");
var flipedDict = sourceDict.FlipKeyValue();
ConsoleWriteDictionary(() => flipedDict);
//実行時エラー　複数のKeyに同じValueがあった場合、入れ替えるとKeyが重複してしまう
//sourceDict["B"] = 10;
//flipedDict = sourceDict.FlipKeyValue();
```

```console:デモ実行結果
# FlipKeyValue
flipedDict: { 10 = A, 20 = B, }
```


## DistinctByKey
LINQで加工した後に、Keyが重複した時などはそのままDictionaryにするとKeyの重複で例外が発生してしまうので、
このDistinctByKeyで重複を除きます。
Keyが重複していたら、先にあるValueが使用されます。

内部でKeyの一致を判定するIEqualityComparer継承クラスを定義して使用しています。

```csharp:実装
public static IEnumerable<KeyValuePair<TKey, TValue>> DistinctByKey<TKey, TValue>(
    this IEnumerable<KeyValuePair<TKey, TValue>> source)
    => source.Distinct(OnlyKeyComparer<TKey, TValue>.Instance);

public class OnlyKeyComparer<Tkey, TValue> : IEqualityComparer<KeyValuePair<Tkey, TValue>>
{
    public static OnlyKeyComparer<Tkey, TValue> Instance { get; } = new OnlyKeyComparer<Tkey, TValue>();

    public bool Equals(KeyValuePair<Tkey, TValue> x, KeyValuePair<Tkey, TValue> y)
        => EqualityComparer<Tkey>.Default.Equals(x.Key, y.Key);

    public int GetHashCode(KeyValuePair<Tkey, TValue> obj) => obj.Key.GetHashCode();
}
```


```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.DistinctByKey)}");
var distinctDict = sourceDict.SelectKey(_ => "X").DistinctByKey().ToDictionary();
ConsoleWriteDictionary(() => distinctDict);
```


```console:デモ実行結果
# DistinctByKey
distinctDict: { X = 10, }
```



## Merge
2つのDictionaryを合流させた1つのDictionaryを返します。

オーバーロード2つのうち、引数が少ない方はKeyが重複していたら、先のDictionaryのValueが使用されます。
もう1つは重複した場合の挙動を細かく制御するためのデリゲートを受け取ります。

```csharp:実装
public static IEnumerable<KeyValuePair<TKey, TValue>> Merge<TKey, TValue>(
    this IEnumerable<KeyValuePair<TKey, TValue>> firstDict,
    IEnumerable<KeyValuePair<TKey, TValue>> secondDict)
    => firstDict.Concat(secondDict).DistinctByKey();

public static IEnumerable<KeyValuePair<TKey, TValue>> Merge<TKey, TValue>(
    this IEnumerable<KeyValuePair<TKey, TValue>> firstDict,
    IEnumerable<KeyValuePair<TKey, TValue>> secondDict,
    Func<TKey, TValue, TValue, TValue> mergerIfDoubled)
    => firstDict.Concat(secondDict)
        .ToLookup(kv => kv.Key)
        .Select(kVs => kVs.Count() <= 1 ? kVs.First() :
            new KeyValuePair<TKey, TValue>(
                kVs.Key,
                mergerIfDoubled(kVs.Key, kVs.First().Value, kVs.Last().Value)));
```



```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.Merge)}");
var secondDict = new Dictionary<string, int>
{
    ["D"] = 400,
    ["B"] = 200,
};
ConsoleWriteDictionary(() => secondDict);
//2つのDictionaryを合流、Keyが重複していたら、先のDictionaryのValueが使用される
var mergedDict = sourceDict.Merge(secondDict).ToDictionary();
var mergedPlusedDict = sourceDict.Merge(secondDict, (k, f, s) => k.Length + f + s).ToDictionary();
ConsoleWriteDictionary(() => mergedDict);
ConsoleWriteDictionary(() => mergedPlusedDict);
```

```console:デモ実行結果
# Merge
secondDict: { D = 400, B = 200, }
mergedDict: { A = 10, B = 20, D = 400, }
mergedPlusedDict: { A = 10, B = 221, D = 400, }
```



## JoinByKey
LINQの中でも強力そうだけど、シグネチャがいかつくて、とっつきづらい`Join`のKeySelctorをDictionaryのKeyに固定したものです。
Mergeは基本的に重複がない2つの同型のDictionary(KeyValuePairコレクション)を1つに加工するものです。
JoinByKeyは2つのDictionaryからKeyが共通な要素を抜き出し、1つに加工するものです。
Mergeと違い2つの入力と出力のValueの型は同じである必要がありません。

```csharp:実装
public static IEnumerable<KeyValuePair<TKey, TValueResult>> JoinByKey<TKey, TValueFirst, TValueSecond, TValueResult>(
    this IEnumerable<KeyValuePair<TKey, TValueFirst>> firstDict,
    IEnumerable<KeyValuePair<TKey, TValueSecond>> secondDict,
    Func<TKey, TValueFirst, TValueSecond, TValueResult> resultSelector)
    => firstDict.Join(secondDict,
                fKV => fKV.Key,
                sKV => sKV.Key,
                (f, s) => new KeyValuePair<TKey, TValueResult>(f.Key, resultSelector(f.Key, f.Value, s.Value)));
```


```csharp:デモコード
sourceDict = CreateSourceDictionary();
Console.WriteLine($"# {nameof(DictionaryExtension.JoinByKey)}");
var secondDict = new Dictionary<string, bool>
{
    ["B"] = true,
    ["D"] = false,
};
ConsoleWriteDictionary(() => secondDict);
var joinedDict = sourceDict.JoinByKey(secondDict, (k, f, s) => k.Length + f + (s ? 100 : -100)).ToDictionary();
ConsoleWriteDictionary(() => joinedDict);
```


```console:デモ実行結果
# JoinByKey
secondDict: { B = True, D = False, }
joinedDict: { B = 121, }
```

# 注意点
多くのメソッドで汎用性を持たせるために`IDictionary<TKey, TValue>`を使用していますが、パフォーマンス面では`Dictionary<TKey, TValue>`を使用したほうが良いです。

参考：http://www.nimaara.com/2016/03/06/beware-of-the-idictionary-tkey-tvalue/

# あとがき
こんなに長くなる予定じゃなかった。
こんなのもあるかも、これがあるならあれも、とかやってたらこんな量に。

# 環境
VisualStudio2017
.NET Framework 4.7
C#7