Sözlükleri C # ile birleştirme


493

2 veya daha fazla sözlük birleştirmenin en iyi yolu nedir (Dictionary<T1,T2>C # ' ) ? (LINQ gibi 3.0 özellik iyi).

Çizgiler boyunca bir yöntem imzası düşünüyorum:

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

veya

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

EDIT: JaredPar ve Jon Skeet'ten harika bir çözüm aldım, ancak yinelenen anahtarları işleyen bir şey düşünüyordum. Çarpışma durumunda, tutarlı olduğu sürece hangi değerin dikte kaydedildiği önemli değildir.


174
İlgisiz, ancak yinelenen anahtar denetimleri olmadan sadece iki sözlük birleştirmek isteyen herkes için, bu güzel çalışıyor:dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
Benjol

6
@ Benjol bunu cevap bölümüne
ekleyebilirdiniz

4
Clojure'un C # ile birleşmesi:dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
Bruce

@Benjol: ile ilk sözlükteki girişleri tercih ederek yinelemeleri ortadan kaldırın dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value).
Suncat2000

Yanıtlar:


320

Bu kısmen, kopyalarla karşılaşırsanız ne olmak istediğinize bağlıdır. Örneğin, şunları yapabilirsiniz:

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

Yinelenen anahtarlar alırsanız bu bir istisna atar.

EDIT: ToLookup kullanıyorsanız, anahtar başına birden çok değeri olan bir arama alırsınız. Sen olabilir sonra sözlüğe o dönüştürmek:

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

Biraz çirkin - ve verimsiz - ancak kod açısından bunu yapmanın en hızlı yolu. (Kabul etmedim.)

Kendi ToDictionary2 uzantısı yönteminizi yazabilirsiniz (daha iyi bir adla, ama şimdi bir tane düşünmek için zamanım yok) - yapmak çok zor değil, sadece yinelenen anahtarların üzerine yazmak (veya yoksaymak). Önemli olan (bence) SelectMany kullanmak ve bir sözlüğün anahtar / değer çiftleri üzerinde yinelemeyi desteklediğini fark etmektir.


3
Değerleri yalnızca ilk sözlükten almak yerine birleştirmek için Jon Skeet'in düzenlenmiş cevabında group => group.First () öğesini grup => group.SelectMany (value => value) ile değiştirebilirsiniz.
andreialecu

3
Şimdi GroupBy'nin ToLookup'tan daha uygun olacağını merak ediyorum. Ben ILookup sadece bir dizinleyici, boyut özelliği ekler ve IGrouping üstüne yöntem içerir - bu yüzden biraz daha hızlı olması gerekiyor?
25'te toong

2
@toong: Dürüst olmak gerekirse ya iyi olmalı. Kullanmak GroupBygerçekten biraz daha verimli olabilir - ancak her iki şekilde de önemli olacağından şüpheliyim.
Jon Skeet

1
@Joe: ToDictionaryYöntem çağrısına karşılaştırıcı sağlayarak yeterince basit . Daha sık karşılaşılan durum için cevabı daha basit tutmayı tercih ederim ve özel bir karşılaştırıcıya ihtiyaç duyan herkesin ilgili aşırı yükü bulabileceğini umuyorum.
Jon Skeet

1
@Serge: Kesin gereksinimleri olan yeni bir soru sormanızı öneririm.
Jon Skeet

265

Bunu şöyle yaparım:

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

Basit ve kolay. Bu blog gönderisine göre , temel uygulaması numaralandırıcıdan ziyade dizinlere göre öğelere eriştiği için çoğu döngüye göre daha hızlıdır (bu cevaba bakın) .

Çoğaltmalar varsa elbette bir istisna atar, bu yüzden birleştirmeden önce kontrol etmeniz gerekir.


169
Kopyalar önemliyse kullanın dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value). Bu yoldan alınan değerler, dictionaryFromolası bir anahtarın değerini geçersiz kılar.
okrumnow

7
Bunun bir döngüden daha hızlı olması olası değildir - ToList () işlevinin sözlüğü gerçekten kopyaladığını unutursunuz. Yine de daha hızlı kod! ;-)
Søren Boisen

6
Bu daha hızlı .. ToList () aslında sözlüğü kopyalamayacağından, içindeki öğelerin referanslarını kopyalar. Temelde liste nesnesinin kendisi bellekte yeni bir adrese sahip olacak, nesneler aynı !!
GuruC

4
Blog yazısı kayboldu ama yay Wayback makinesi: web.archive.org/web/20150311191313/http://diditwith.net/2006/10/…
CAD bloke

1
Hmm, daha iyi Jon Skeet cevabı herhalde.
Sнаđошƒаӽ

102

Birden fazla anahtar varsa ("kaldırıcı" anahtarlar "kaldırıcı" anahtarların yerini alır) patlamaz, birtakım sözlükleri birleştirebilir (istenirse) ve türü korur (anlamlı bir varsayılan genel kurucu gerektirdiği kısıtlama ile):

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;
    }

}

Aferin. Tam da ihtiyacım olan buydu. Jeneriklerin güzel kullanımı. Sözdiziminin biraz garip olduğunu kabul etti, ancak bu konuda yapabileceğiniz hiçbir şey yok.
Peter M

2
Bu çözümü beğendim, ancak bir uyarı var: eğer this T mesözlük kullanarak new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)veya benzeri bir şey ilan edilirse , ortaya çıkan sözlük aynı davranışı sürdürmez.
João Portela

3
Eğer yaparsanız mebir Dictionary<K,V>, daha sonra yapabilirsiniz var newMap = new Dictionary<TKey, TValue>(me, me.Comparer);ve ayrıca ekstra önlemek Ttürü parametresi.
ANeves

1
Birinin bu canavarı nasıl kullanacağına bir örneği var mı?
Tim

1
@Tim: Aşağıdaki canavar için bir örnek ekledim, çözümü ANeves tarafından ekledim ve sonuç daha evcil bir canavar :)
keni

50

Önemsiz çözüm şöyle olurdu:

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;
}

22

Takip etmeyi dene

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);
}

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

1
Merak ediyorum - sadece Birlik işe yaramaz mıydı? foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))Üretim kodunda kullanmak, herhangi bir olumsuzluk varsa lütfen bana bildirin! :)
Mars Robertson

1
@Michal: Birlik, IEnumerable<KeyValuePair<TKey, TValue>>eşitliğin anahtar ve değer eşitliği ile tanımlandığı bir yere geri dönecektir (alternatiflere izin verilmesi için aşırı yük vardır). Bu, her şeyin gerekli olduğu bir foreach döngüsü için iyidir, ancak sözlüğü gerçekten istediğiniz durumlar vardır.
Timothy

15

Partiye çok geç kaldım ve belki de bir şey eksik, ama ya yinelenen anahtar yoksa ya da OP'nin dediği gibi, "Çarpışma durumunda, hangi değerin dikte olduğu sürece önemli değil. tutarlı, "bunun sorunu ne (D2'yi D1 ile birleştirmek)?

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

Yeterince basit görünüyor, belki çok basit, bir şey eksik mi diye merak ediyorum. Bu, yinelenen anahtarlar olmadığını bildiğim bazı kodlarda kullanıyorum. Yine de test yapıyorum, bu yüzden daha sonra öğrenmek yerine bir şeye bakıp bakmadığımı bilmek isterim.


4
En temiz ve en okunabilir çözümlerden biri, diğerlerinin kullandığı ToList'ten () imo ve ayrıca kaçınır.
404

15

Aşağıdakiler benim için çalışıyor. Kopyalar varsa, dictA'nın değerini kullanır.

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]);
}

@EsbenSkovPedersen Bunu cevapladığımda karşılaştığımı sanmıyorum (yeni C # dev)
Ethan Reesor


@ om471987 Evet, Guid bir sınıf değil bir yapıdır, bu yüzden mantıklıdır. Sanırım başlangıçta bu maddeyi eklemediğimde bir sorunum vardı. Artık nedenini hatırlamıyorum.
Ethan Reesor

10

İşte kullandığım bir yardımcı fonksiyon:

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);
        }
    }
}

Burada listelenen birçok harika çözüm var, ancak bunun en basitliğini basitçe seviyorum. Sadece kişisel tercihim.
jason

3
if(first == null)o zaman bu mantık işe yaramaz çünkü iade firstedilmez refve iade edilmez. Ve bu refbir arayüz örneği olarak bildirildiği için olamaz ; hata denetimini kaldırmanız veya bir sınıf örneği olarak bildirmeniz veya yalnızca yeni bir sözlük (uzantı yöntemi olmadan) döndürmeniz gerekir.
Grault

@Jesdisciple - önerinizi göre sorunu kaldırıldı
Andrew Harry

8

Seçenek 1: Bu, her iki sözlükte de yinelenen anahtarın olmadığından eminseniz, ne olmak istediğinize bağlıdır. yapabileceğinizden daha fazla:

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

Not: Sözlüklerde yinelenen anahtarlar alırsanız bu hata verir.

Seçenek 2: Yinelenen anahtarınız varsa, yinelenen anahtarı where yan tümcesini kullanarak işlemeniz gerekir.

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

Not: Yinelenen anahtar alınmayacaktır. eğer yinelenen bir anahtar varsa, dictionary1'in anahtarı alır.

Seçenek 3: ToLookup kullanmak istiyorsanız. anahtar başına birden çok değere sahip olabilen bir arama alırsınız. Bu aramayı bir sözlüğe dönüştürebilirsiniz:

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

6

paramsAşırı yük eklemeye ne dersiniz ?

Ayrıca, bunları IDictionarymaksimum esneklik için yazmalısınız.

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);
}

4
DictionaryExtensions sınıfını daha da güzel yapabileceğinizi belirterek, buradaki diğer cevaplara bir çeşit ekleme yapıyor. Belki de bu soru bir wiki veya git için iade edilen bir sınıf olmalı ...
Chris Moschini

5

Sözlük anahtar aramaları ve silmelerin hash işlemleri olduğu için performansı göz önüne alındığında ve sorunun ifadesinin en iyi yol olduğunu düşünürsek, aşağıda mükemmel bir geçerli yaklaşım olduğunu düşünüyorum ve diğerleri biraz fazla karmaşık, IMHO.

    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);
        }
    }

VEYA çok iş parçacıklı bir uygulamada çalışıyorsanız ve sözlüğünüzün yine de iş parçacığı güvenli olması gerekiyorsa, bunu yapmanız gerekir:

    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);
        }
    }

Daha sonra bir sözlük numaralandırma işlemek için bunu sarmak olabilir. Ne olursa olsun, ~ O (3n) 'ye bakıyorsunuz (tüm koşullar mükemmel), çünkü perde arkasında .Add()ek, gereksiz ama pratik olarak ücretsiz olacak Contains(). Çok daha iyi olduğunu sanmıyorum.

Büyük koleksiyonlardaki ekstra işlemleri sınırlamak Countistiyorsanız, birleştirmek üzere olduğunuz her sözlüğü özetlemeli ve hedef sözlüğün kapasitesini buna ayarlamalı ve bu da daha sonra yeniden boyutlandırma maliyetini ortadan kaldırmalıdır. Yani, son ürün böyle bir şey ...

    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>Bu yönteme dikkatimi çektiğimi unutmayın ... çünkü eğer bir alırsanız IEnumerable<T>, kendinizi aynı setin birden fazla numaralandırmasına açtınız, bu da ertelenmiş bir LINQ'dan sözlük koleksiyonunuzu aldıysanız çok maliyetli olabilir. Beyan.


4

Yukarıdaki cevaplara dayanarak, arayan kişinin kopyaları işlemesine izin vermek için bir Func parametresi eklemek:

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));
}

4

Parti şimdi çoktan öldü, ama işte kullanıcı166390'ın uzantı kütüphaneme giren "geliştirilmiş" bir versiyonu. Bazı ayrıntıların yanı sıra, birleştirilmiş değeri hesaplamak için bir temsilci ekledim.

/// <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;
}

2

@Tim: Bir yorum olmalı, ancak yorumlar kod düzenlemeye izin vermiyor.

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);

Not: @aneves tarafından yapılan değişikliği @Andrew Orsich tarafından çözüme uyguladım, böylece MergeLeft şu şekilde görünüyor:

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;
    }

Kendime yazdıklarıma yakın. Bu, ofc, sadece Dictionarytipler için çalışır . Diğer Sözlük türleri ( ConcurrentDictionary, ReadOnlyDictionaryvb.) İçin aşırı yüklemeler eklenebilir Yeni bir liste oluşturmayı düşünmüyorum ve concat kişisel olarak gerekli. Önce params dizisini tekrarladım, sonra her sözlüğün her bir KVP'sini tekrarladım. Geri dönüş görmüyorum.
ezmek

ID yerine Dictionary'i kullanabilirsiniz
Mariusz Jamro

Üzerinde Dictionaryuzantı yöntemini kullansanız bile , her zaman bir nesne elde edersiniz ConcurrentDictionary. Bu, hataların izini zorlaştırabilir.
Marcel

2

Bunun eski bir soru olduğunu biliyorum, ama şimdi LINQ'umuz olduğu için bunu tek bir satırda yapabilirsiniz

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

veya

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

@Cruces için üzgünüm, ancak ilk örneğiniz yinelenen yanıt stackoverflow.com/a/6695211/704808 ve ikinci örneğiniz her öğeyi ekledikten sonra genişletilmiş IEnumerable <KeyValuePair <string, string >> ' yi atmaya devam ediyor mergee.
savak

2

C # için yeni olan karmaşık cevapları görmekten korktum.

İşte bazı basit cevaplar.
D1, d2, vb.

örnek 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
}

ÖRNEK 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
}

Daha karmaşık senaryolar için diğer yanıtlara bakın.
Umut etmek bu yardım etmek.


2
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; });
}

Atlayabilir / yok sayabilir (varsayılan) veya yinelenenlerin üzerine yazabilirsiniz: Ve Bob, amcanız Linq performansı hakkında aşırı telaşlı olmamanız, ancak benim yaptığım gibi özlü bakım kodunu tercih etmenizi sağlar: bu durumda zorlamak için varsayılan MergeKind.SkipDuplicates'i kaldırabilirsiniz Arayan için bir seçim yapın ve geliştiricinin sonuçların ne olacağını bilmesini sağlayın!


Bir bool ne zaman gereksiz ikili numaralandırma olmadan aşağıda rafine cevap ...
mattjs

2

'Ekle' adlı bir uzantı yöntemi kullanırsanız, gerektiği gibi sözlükleri birleştirmek için koleksiyon başlatıcıları kullanabileceğinizi unutmayın:

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" }
};

1

Bir uzatma yöntemi kullanarak birleştirme. Yinelenen anahtarlar olduğunda istisna atmaz, ancak bu anahtarları ikinci sözlükteki anahtarlarla değiştirir.

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;
    }
}

Kullanımı:

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

1

Önceki yanıtımla karşılaştırıldığında kullanımdan sadeleştirilmiş, eğer varsa bir tahribatsız birleşmenin bool varsayılanı veya bir enum kullanmak yerine tamamen doğru yazılması. Herhangi bir meraklı kodu gerekmeden hala kendi ihtiyaçlarıma uygun:

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;
        });
    }
}

0

EqualityComparerÖğeleri farklı bir değer / türle karşılaştırmak için eşleyen bir kullanarak birleştirme . Burada KeyValuePair(sözlük numaralandırırken öğe türü) ile eşleştireceğiz 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();
    }
}

Kullanımı:

// 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);

0

veya:

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);
    }

sonuç yinelenen girişler için "y" nin kazandığı bir birleşmedir.


0
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;
        }

0

@ User166390'dan gelen bir sürüm, büyük / IEqualityComparerküçük harfe duyarlı olmayan anahtar karşılaştırmasına olanak sağlamak için eklenen bir parametreyle yanıt verir.

    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;
    }

0
fromDic.ToList().ForEach(x =>
        {
            if (toDic.ContainsKey(x.Key))
                toDic.Remove(x.Key);
            toDic.Add(x);
        });
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.