C#でのマージ辞書

(C#の2つの以上の辞書をマージしDictionary<T1,T2>ている最良の方法は何ですか)?(同様LINQ 3.0はうまく機能します)。

私は、署名への道を考えています:

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(Dictionary<TKey,TValue>[] dictionaries);

若しくは

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(IEnumerable<Dictionary<TKey,TValue>> dictionaries);

編集: JARE DPARとジョンスキートからクールなソリューションを得るために、私は重複キーを処理するためのものと思っています。衝突の際には、限り、それは一貫しているように、値が辞書に保存されますこれは重要ではありません。


#1階

これは私が使用するヘルパー関数です:

using System.Collections.Generic;
namespace HelperMethods
{
    public static class MergeDictionaries
    {
        public static void Merge<TKey, TValue>(this IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second)
        {
            if (second == null || first == null) return;
            foreach (var item in second) 
                if (!first.ContainsKey(item.Key)) 
                    first.Add(item.Key, item.Value);
        }
    }
}

ハウス#2

上記の回答によると、しかし、のFuncパラメータを追加すると、呼び出し側は重複するアイテムを処理することができます:

public static Dictionary<TKey, TValue> Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> dicts, 
                                                           Func<IGrouping<TKey, TValue>, TValue> resolveDuplicates)
{
    if (resolveDuplicates == null)
        resolveDuplicates = new Func<IGrouping<TKey, TValue>, TValue>(group => group.First());

    return dicts.SelectMany<Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue>>(dict => dict)
                .ToLookup(pair => pair.Key, pair => pair.Value)
                .ToDictionary(group => group.Key, group => resolveDuplicates(group));
}

ハウス#3

党は今死んだが、「改良版」の166390人のユーザーがいる、それが私の拡張に入ってきました。詳細の一部に加えて、私は合併の値を計算するために、デリゲートを追加しました。

/// <summary>
/// Merges a dictionary against an array of other dictionaries.
/// </summary>
/// <typeparam name="TResult">The type of the resulting dictionary.</typeparam>
/// <typeparam name="TKey">The type of the key in the resulting dictionary.</typeparam>
/// <typeparam name="TValue">The type of the value in the resulting dictionary.</typeparam>
/// <param name="source">The source dictionary.</param>
/// <param name="mergeBehavior">A delegate returning the merged value. (Parameters in order: The current key, The current value, The previous value)</param>
/// <param name="mergers">Dictionaries to merge against.</param>
/// <returns>The merged dictionary.</returns>
public static TResult MergeLeft<TResult, TKey, TValue>(
    this TResult source,
    Func<TKey, TValue, TValue, TValue> mergeBehavior,
    params IDictionary<TKey, TValue>[] mergers)
    where TResult : IDictionary<TKey, TValue>, new()
{
    var result = new TResult();
    var sources = new List<IDictionary<TKey, TValue>> { source }
        .Concat(mergers);

    foreach (var kv in sources.SelectMany(src => src))
    {
        TValue previousValue;
        result.TryGetValue(kv.Key, out previousValue);
        result[kv.Key] = mergeBehavior(kv.Key, kv.Value, previousValue);
    }

    return result;
}

#4階

拡張メソッドの併用。そうでない場合は重複キーの例外が代わりに辞書で2番目のキーとこれらのキーの、スローされます。

internal static class DictionaryExtensions
{
    public static Dictionary<T1, T2> Merge<T1, T2>(this Dictionary<T1, T2> first, Dictionary<T1, T2> second)
    {
        if (first == null) throw new ArgumentNullException("first");
        if (second == null) throw new ArgumentNullException("second");

        var merged = new Dictionary<T1, T2>();
        first.ToList().ForEach(kv => merged[kv.Key] = kv.Value);
        second.ToList().ForEach(kv => merged[kv.Key] = kv.Value);

        return merged;
    }
}

使用法:

Dictionary<string, string> merged = first.Merge(second);

ハウス#5

使用EqualityComparerマージEqualityComparer比較する項目は異なる値/タイプにマッピング。ここでは、されますKeyValuePair(辞書列挙型の項目)にマッピングされましたKey

public class MappedEqualityComparer<T,U> : EqualityComparer<T>
{
    Func<T,U> _map;

    public MappedEqualityComparer(Func<T,U> map)
    {
        _map = map;
    }

    public override bool Equals(T x, T y)
    {
        return EqualityComparer<U>.Default.Equals(_map(x), _map(y));
    }

    public override int GetHashCode(T obj)
    {
        return _map(obj).GetHashCode();
    }
}

使用法:

// if dictA and dictB are of type Dictionary<int,string>
var dict = dictA.Concat(dictB)
                .Distinct(new MappedEqualityComparer<KeyValuePair<int,string>,int>(item => item.Key))
                .ToDictionary(item => item.Key, item=> item.Value);

ハウス#6

私は、後半の当事者によOPが言ったように、衝突が発生した場合はそれを保存する値、限り、それをdとして、」、いくつかのことを見逃すことが、重複キーがどちらかがない場合、またはかもしれないdictの一貫性を重要ではありません。 「何が(D2がD1にマージ)これで間違っていますか?

foreach (KeyValuePair<string,int> item in D2)
            {
                 D1[item.Key] = item.Value;
            }

これは、私が何かを逃した場合、私は疑問に思う、多分あまりにもシンプルなので、簡単なようです。これは、私は重複したキーが存在しないことを知って、私はいくつかのコードで使用されるものです。しかし、私はまだテストされています、私は何かを見落としそうならば、私は後で見つかったのではなく、今知りたいです。


#7の構築

以下は、私に適用されます。重複したエントリがある場合、それは傍論の値を使用します。

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(this IDictionary<TKey, TValue> dictA, IDictionary<TKey, TValue> dictB)
    where TValue : class
{
    return dictA.Keys.Union(dictB.Keys).ToDictionary(k => k, k => dictA.ContainsKey(k) ? dictA[k] : dictB[k]);
}

#8の構築

Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);

ハウス#9

考慮するキーを見つけて削除する辞書をする、パフォーマンスを彼らはハッシュされているので、言語の問題がある考慮して最善の方法は、私は次のように完全に有効な方法で、少し複雑すぎる他のA、私見だと思います。

    public static void MergeOverwrite<T1, T2>(this IDictionary<T1, T2> dictionary, IDictionary<T1, T2> newElements)
    {
        if (newElements == null) return;

        foreach (var e in newElements)
        {
            dictionary.Remove(e.Key); //or if you don't want to overwrite do (if !.Contains()
            dictionary.Add(e);
        }
    }

それとも、あなたはマルチスレッドアプリケーションでの作業、そしてあなたはスレッドセーフとにかく辞書、あなたがすべき必要がある場合:

    public static void MergeOverwrite<T1, T2>(this ConcurrentDictionary<T1, T2> dictionary, IDictionary<T1, T2> newElements)
    {
        if (newElements == null || newElements.Count == 0) return;

        foreach (var ne in newElements)
        {
            dictionary.AddOrUpdate(ne.Key, ne.Value, (key, value) => value);
        }
    }

次に、あなたが列挙プロセスの辞書を作るために、それをラップすることができます。いずれにせよ、あなたが見ている〜O(3N)(すべての条件が完全である)、など.Add()の余分な、不要な舞台裏で行うが、実際には無料ですContains()私はそれがはるかに良いではないだろうと思います。

あなたが大規模なコレクションに追加の操作を制限したい場合は、マージされた各辞書の要約である必要がありCount、調整コストが大きさを避けるためにした後、その値に目標容量セットの辞書。したがって、最終製品がそうである......

    public static IDictionary<T1, T2> MergeAllOverwrite<T1, T2>(IList<IDictionary<T1, T2>> allDictionaries)
    {
        var initSize = allDictionaries.Sum(d => d.Count);
        var resultDictionary = new Dictionary<T1, T2>(initSize);
        allDictionaries.ForEach(resultDictionary.MergeOverwrite);
        return resultDictionary;
    }

私は受け入れてください。なお、IList<T>使用すると、1つを受け入れる場合、主な理由は...この方法をIEnumerable<T>使用すると、これは遅れから非常に高価なあなたLINQ文の可能性があれば、あなたは、同じグループの彼らの複数の列挙を開設しました辞書のコレクション。


ハウス#10

複数のキーがある場合は、複数の辞書をマージする(必要な場合)と(それが意味のある既定のパブリックコンストラクタを必要とする制限を)タイプを保持することができ、それが爆発しないだろう、(「righter」キーは、キー「lefter」置き換えます):

public static class DictionaryExtensions
{
    // Works in C#3/VS2008:
    // Returns a new dictionary of this ... others merged leftward.
    // Keeps the type of 'this', which must be default-instantiable.
    // Example: 
    //   result = map.MergeLeft(other1, other2, ...)
    public static T MergeLeft<T,K,V>(this T me, params IDictionary<K,V>[] others)
        where T : IDictionary<K,V>, new()
    {
        T newMap = new T();
        foreach (IDictionary<K,V> src in
            (new List<IDictionary<K,V>> { me }).Concat(others)) {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K,V> p in src) {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

}

ハウス#11

@Tim:それは検討する必要がありますが、レビューはコードの編集を許可しません。

Dictionary<string, string> t1 = new Dictionary<string, string>();
t1.Add("a", "aaa");
Dictionary<string, string> t2 = new Dictionary<string, string>();
t2.Add("b", "bee");
Dictionary<string, string> t3 = new Dictionary<string, string>();
t3.Add("c", "cee");
t3.Add("d", "dee");
t3.Add("b", "bee");
Dictionary<string, string> merged = t1.MergeLeft(t2, t2, t3);

注:私は@ANevesの変更が@Andrew Orsichソリューションに適用されますが、そのためMergeLeftは次のようになります。

public static Dictionary<K, V> MergeLeft<K, V>(this Dictionary<K, V> me, params IDictionary<K, V>[] others)
    {
        var newMap = new Dictionary<K, V>(me, me.Comparer);
        foreach (IDictionary<K, V> src in
            (new List<IDictionary<K, V>> { me }).Concat(others))
        {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K, V> p in src)
            {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

ハウス#12

自明な解は次のようになります。

using System.Collections.Generic;
...
public static Dictionary<TKey, TValue>
    Merge<TKey,TValue>(IEnumerable<Dictionary<TKey, TValue>> dictionaries)
{
    var result = new Dictionary<TKey, TValue>();
    foreach (var dict in dictionaries)
        foreach (var x in dict)
            result[x.Key] = x.Value;
    return result;
}

ハウス#13

以下のことを試してください

static Dictionary<TKey, TValue>
    Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> enumerable)
{
    return enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value);
}

ハウス#14

それはあなたが繰り返し物事に遭遇した上で部分的に依存しています。たとえば、あなたはこれを行うことができます。

var result = dictionaries.SelectMany(dict => dict)
                         .ToDictionary(pair => pair.Key, pair => pair.Value);

あなたが任意の重複したキーを取得する場合、それが爆発します。

編集:あなたはToLookupを使用する場合は、あなたが外観を得るだろう、各キーは、複数の値を持つことができます。次に、あなたができる辞書に変換します。

var result = dictionaries.SelectMany(dict => dict)
                         .ToLookup(pair => pair.Key, pair => pair.Value)
                         .ToDictionary(group => group.Key, group => group.First());

非効率的な - - それは少し醜いですが、これは、コードの面でそれを行うための最速の方法です。(確かに、私がテストしていません。)

あなたはもちろん、独自のToDictionary2拡張メソッドを書くことができます(よりよい名前が、私は考える時間を持っていない) - それはちょうどカバーに、行うのは難しいことではありません(または無視)重複したキーを。(私の意見では)重要な点は、SelectManyを使用することで、辞書担体は、キー/値のペアの繰り返していることに気づきました。


ハウス#15

追加する方法params重い負荷を?

また、あなたはそれらを入力する必要がありIDictionary、最大の柔軟性を得るために。

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(IEnumerable<IDictionary<TKey, TValue>> dictionaries)
{
    // ...
}

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(params IDictionary<TKey, TValue>[] dictionaries)
{
    return Merge((IEnumerable<TKey, TValue>) dictionaries);
}

ハウス#16

私は、これは古い問題である知っているが、我々は今、LINQを持っているので、あなたは、このような単一の行でそれを行うことができます

Dictionary<T1,T2> merged;
Dictionary<T1,T2> mergee;
mergee.ToList().ForEach(kvp => merged.Add(kvp.Key, kvp.Value));

若しくは

mergee.ToList().ForEach(kvp => merged.Append(kvp));

ハウス#17

次のいずれか

public static IDictionary<TKey, TValue> Merge<TKey, TValue>( IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
    {
        return x
            .Except(x.Join(y, z => z.Key, z => z.Key, (a, b) => a))
            .Concat(y)
            .ToDictionary(z => z.Key, z => z.Value);
    }

結果は、勝つために重複したエントリ「y」を共同で。


ハウス#18

怖いは、C#に精通していない複雑な答えを、確認してください。

ここではいくつかの簡単な答えがあります。
合わせD1、D2とハンドル他の辞書重複キー(「B」以下の例では)。

例1

{
    // 2 dictionaries,  "b" key is common with different values

    var d1 = new Dictionary<string, int>() { { "a", 10 }, { "b", 21 } };
    var d2 = new Dictionary<string, int>() { { "c", 30 }, { "b", 22 } };

    var result1 = d1.Concat(d2).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.First().Value);
    // result1 is  a=10, b=21, c=30    That is, took the "b" value of the first dictionary

    var result2 = d1.Concat(d2).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.Last().Value);
    // result2 is  a=10, b=22, c=30    That is, took the "b" value of the last dictionary
}

例2

{
    // 3 dictionaries,  "b" key is common with different values

    var d1 = new Dictionary<string, int>() { { "a", 10 }, { "b", 21 } };
    var d2 = new Dictionary<string, int>() { { "c", 30 }, { "b", 22 } };
    var d3 = new Dictionary<string, int>() { { "d", 40 }, { "b", 23 } };

    var result1 = d1.Concat(d2).Concat(d3).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.First().Value);
    // result1 is  a=10, b=21, c=30, d=40    That is, took the "b" value of the first dictionary

    var result2 = d1.Concat(d2).Concat(d3).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.Last().Value);
    // result2 is  a=10, b=23, c=30, d=40    That is, took the "b" value of the last dictionary
}

より複雑なシナリオでは、他の回答を参照してください。
お役に立つといいのですが。


ハウス#19

using System.Collections.Generic;
using System.Linq;

public static class DictionaryExtensions
{
    public enum MergeKind { SkipDuplicates, OverwriteDuplicates }
    public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, MergeKind kind = MergeKind.SkipDuplicates) =>
        source.ToList().ForEach(_ => { if (kind == MergeKind.OverwriteDuplicates || !target.ContainsKey(_.Key)) target[_.Key] = _.Value; });
}

あなたは(デフォルト)または上書き重複するアイテムを無視/スキップすることができます:あなたはあまりにもうるさいLINQの性能でない場合は、ボブはあなたの叔父ですが、私は、保守コードのシンプルさを好むものです。このケースでは、デフォルトを削除することができますMergeKind.SkipDuplicatesは、開発者が、結果がどうなるかを実現することができ、発信者を選ぶ強制します!


ハウス#20

public static IDictionary<K, V> AddRange<K, V>(this IDictionary<K, V> one, IDictionary<K, V> two)
        {
            foreach (var kvp in two)
            {
                if (one.ContainsKey(kvp.Key))
                    one[kvp.Key] = two[kvp.Key];
                else
                    one.Add(kvp.Key, kvp.Value);
            }
            return one;
        }

ハウス#21

オプション1:あなたが起こるしたいのかに応じて2つの重複キーを決定するために辞書にない場合。あなたが行うことができますより:

var result = dictionary1.Union(dictionary2).ToDictionary(k => k.Key, v => v.Value)

注:すべての重複キーが辞書に表示された場合、それはエラーがスローされます。

オプション2:あなたが重複したキーを使用することができる場合は、重複キーを処理するためにwhere句を使用する必要があります。

var result = dictionary1.Union(dictionary2.Where(k => !dictionary1.ContainsKey(k.Key))).ToDictionary(k => k.Key, v => v.Value)

注:これは、重複キーを取得できません。重複キーがある場合は、キーdictionary1を取得します。

オプション3:あなたはToLookupを使用したい場合。その後、各キーは、複数の値を持つことができ、外観を取得します。あなたは、変換辞書を見つけることができます。

var result = dictionaries.SelectMany(dict => dict)
                         .ToLookup(pair => pair.Key, pair => pair.Value)
                         .ToDictionary(group => group.Key, group => group.First());

ハウス#22

バージョン@追加の使用からuser166390 IEqualityComparerキーの比較を可能にするために、解答へのパラメータは、大文字と小文字は区別されません。

    public static T MergeLeft<T, K, V>(this T me, params Dictionary<K, V>[] others)
        where T : Dictionary<K, V>, new()
    {
        return me.MergeLeft(me.Comparer, others);
    }

    public static T MergeLeft<T, K, V>(this T me, IEqualityComparer<K> comparer, params Dictionary<K, V>[] others)
        where T : Dictionary<K, V>, new()
    {
        T newMap = Activator.CreateInstance(typeof(T), new object[] { comparer }) as T;

        foreach (Dictionary<K, V> src in 
            (new List<Dictionary<K, V>> { me }).Concat(others))
        {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K, V> p in src)
            {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

ハウス#23

fromDic.ToList().ForEach(x =>
        {
            if (toDic.ContainsKey(x.Key))
                toDic.Remove(x.Key);
            toDic.Add(x);
        });

ハウス#24

私の前の回答、ブールデフォルトの代わりに列挙を使用しての非破壊マージ(存在する場合)または完全に覆わ(trueの場合)に比べて簡素化されました。それはまだ任意のより高度なコードを必要とせずに、自分のニーズに合います:

using System.Collections.Generic;
using System.Linq;

public static partial class Extensions
{
    public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, bool overwrite = false)
    {
        source.ToList().ForEach(_ => {
            if ((!target.ContainsKey(_.Key)) || overwrite)
                target[_.Key] = _.Value;
        });
    }
}

ハウス#25

あなたは、「追加」という名前の拡張メソッドを使用する場合は、次のように多くの辞書として、必要に応じて、あなたが組み合わせ初期化子コレクションを使用できることに注意してください:

public static void Add<K, V>(this Dictionary<K, V> d, Dictionary<K, V> other) {
  foreach (var kvp in other)
  {
    if (!d.ContainsKey(kvp.Key))
    {
      d.Add(kvp.Key, kvp.Value);
    }
  }
}


var s0 = new Dictionary<string, string> {
  { "A", "X"}
};
var s1 = new Dictionary<string, string> {
  { "A", "X" },
  { "B", "Y" }
};
// Combine as many dictionaries and key pairs as needed
var a = new Dictionary<string, string> {
  s0, s1, s0, s1, s1, { "C", "Z" }
};

ハウス#26

私はそうするでしょう。

dictionaryFrom.ToList().ForEach(x => dictionaryTo.Add(x.Key, x.Value));

シンプル。よると、このブログの記事、その基礎となるインデックスはなく列挙子要素によるアクセスのためので、それは、より速く、最もよりもループ(答えを参照します)

もちろん、重複がある場合、それは例外がスローされますので、あなたは、合併前にチェックする必要があります。

オリジナルの記事は、0公表 ウォンの賞賛2 ビュー7481を

おすすめ

転載: blog.csdn.net/asdfgh0077/article/details/104104472