Menggabungkan kamus dalam C #

493

Apa cara terbaik untuk menggabungkan 2 kamus atau lebih (Dictionary<T1,T2> ) dalam C #? (3.0 fitur seperti LINQ baik-baik saja).

Saya sedang memikirkan tanda tangan metode di sepanjang baris:

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

atau

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

EDIT: Mendapat solusi keren dari JaredPar dan Jon Skeet, tapi saya memikirkan sesuatu yang menangani kunci duplikat. Dalam kasus tabrakan, tidak masalah nilai mana yang disimpan ke dikt selama itu konsisten.

orip
sumber
175
Tidak terkait, tetapi bagi siapa pun yang ingin menggabungkan hanya dua kamus tanpa duplikat kunci cek, ini berfungsi dengan baik:dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
Benjol
6
@Benjol Anda bisa menambahkan ini di bagian jawaban
M.Kumaran
4
Penggabungan Clojure dalam C #:dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
Bruce
@Benjol: hilangkan duplikat dengan memilih entri dari kamus pertama dengan dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value).
Suncat2000

Jawaban:

321

Ini sebagian tergantung pada apa yang Anda inginkan terjadi jika Anda mengalami duplikat. Misalnya, Anda dapat melakukan:

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

Itu akan menghasilkan pengecualian jika Anda mendapatkan kunci duplikat.

EDIT: Jika Anda menggunakan ToLookup maka Anda akan mendapatkan pencarian yang dapat memiliki beberapa nilai per kunci. Anda kemudian dapat mengonversinya menjadi kamus:

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

Agak jelek - dan tidak efisien - tetapi ini adalah cara tercepat untuk melakukannya dalam hal kode. (Aku belum mengujinya, diakui.)

Anda dapat menulis metode ekstensi ToDictionary2 Anda sendiri tentu saja (dengan nama yang lebih baik, tapi saya tidak punya waktu untuk memikirkannya sekarang) - tidak terlalu sulit untuk dilakukan, hanya menimpa (atau mengabaikan) kunci duplikat. Bit penting (menurut saya) adalah menggunakan SelectMany, dan menyadari bahwa kamus mendukung iterasi dari pasangan kunci / nilainya.

Jon Skeet
sumber
3
Untuk benar-benar menggabungkan nilai alih-alih hanya mengambilnya dari kamus pertama, Anda dapat mengganti group => group.First () dengan group => group.SelectMany (value => value) dalam jawaban yang diedit Jon Skeet.
andreialecu
3
Sekarang saya bertanya-tanya bahwa GroupBy akan lebih cocok daripada ToLookup? Saya pikir ILookup baru saja menambahkan indexer, properti ukuran dan berisi metode di atas IGrouping - jadi harus sedikit lebih cepat?
toong
2
@ Toong: Jujur saja. Menggunakan GroupBymemang bisa sedikit lebih efisien - tapi saya ragu itu akan menjadi signifikan.
Jon Skeet
1
@ Jo: Itu cukup sederhana dengan menyediakan pembanding untuk ToDictionarypemanggilan metode. Saya lebih suka menyimpan jawabannya lebih sederhana untuk kasus yang lebih umum, dan saya berharap siapa pun yang membutuhkan pembanding khusus dapat menemukan kelebihan yang relevan.
Jon Skeet
1
@Serge: Saya sarankan Anda mengajukan pertanyaan baru dengan persyaratan yang tepat.
Jon Skeet
265

Saya akan melakukannya seperti ini:

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

Sederhana dan mudah. Menurut posting blog ini , ini bahkan lebih cepat daripada kebanyakan loop karena implementasi yang mendasarinya mengakses elemen dengan indeks daripada enumerator (lihat jawaban ini) .

Tentu saja akan mengeluarkan pengecualian jika ada duplikat, jadi Anda harus memeriksa sebelum bergabung.

Jonas Stensved
sumber
169
Jika duplikat penting, gunakan dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value). Dengan cara ini nilai dari dictionaryFrommenimpa nilai untuk kunci yang mungkin ada.
okrumnow
7
Ini tidak mungkin lebih cepat daripada loop - Anda lupa bahwa ToList () sebenarnya menyalin kamus. Lebih cepat ke kode! ;-)
Søren Boisen
6
Ini lebih cepat .. Karena ToList () sebenarnya tidak berakhir menyalin kamus, itu hanya menyalin referensi elemen di dalamnya. Pada dasarnya objek daftar itu sendiri akan memiliki alamat baru di memori, objeknya sama !!
GuruC
4
Posting blog menghilang tetapi mesin yay Wayback: web.archive.org/web/20150311191313/http://diditwith.net/2006/10/…
CAD bloke
1
Hmm, lebih baik daripada jawaban Jon Skeet, saya kira.
Sнаđошƒаӽ
102

Ini tidak meledak jika ada beberapa kunci ("righter" kunci menggantikan "lefter" kunci), dapat menggabungkan sejumlah kamus (jika diinginkan) dan mempertahankan jenisnya (dengan batasan yang memerlukan konstruktor publik default yang bermakna):

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

}
Dave
sumber
Sudah selesai dilakukan dengan baik. Inilah yang saya butuhkan. Penggunaan generik yang bagus. Setuju bahwa sintaks sedikit canggung, tetapi tidak ada yang dapat Anda lakukan tentang itu.
Peter M
2
Saya menyukai solusi ini, tetapi ada peringatan: jika this T mekamus dinyatakan menggunakan new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)atau sesuatu yang serupa, kamus yang dihasilkan tidak akan mempertahankan perilaku yang sama.
João Portela
3
Jika Anda membuat mesebuah Dictionary<K,V>, Anda kemudian dapat melakukan var newMap = new Dictionary<TKey, TValue>(me, me.Comparer);, dan Anda juga menghindari tambahan Tparameter tipe.
ANeves
2
Apakah seseorang memiliki contoh cara menggunakan binatang ini?
Tim
2
@Tim: Saya menambahkan contoh untuk binatang buas di bawah ini, saya menambahkan solusi oleh ANeves dan hasilnya adalah binatang buas yang lebih
terdomestikasi
50

Solusi sepele adalah:

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;
}
orip
sumber
22

Coba yang berikut ini

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);
}
JaredPar
sumber
19
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);
ctrlalt313373
sumber
1
Hanya ingin tahu - bukankah Union hanya berfungsi? foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))Menggunakannya dalam kode produksi, jika ada kelemahan silakan beri tahu saya! :)
Mars Robertson
1
@Michal: Serikat pekerja akan mengembalikan IEnumerable<KeyValuePair<TKey, TValue>>tempat kesetaraan didefinisikan oleh kesetaraan kunci dan nilai (ada kelebihan untuk memungkinkan alternatif). Ini bagus untuk loop foreach, di mana ini semua yang diperlukan, tetapi ada kasus di mana Anda benar-benar ingin kamus.
Timothy
15

Saya sangat terlambat ke pesta dan mungkin melewatkan sesuatu, tetapi jika tidak ada kunci duplikat atau, seperti kata OP, "Dalam kasus tabrakan, tidak masalah nilai yang disimpan ke dikt selama itu konsisten, "apa yang salah dengan ini (menggabungkan D2 ke D1)?

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

Tampaknya cukup sederhana, mungkin terlalu sederhana, saya ingin tahu apakah saya kehilangan sesuatu. Inilah yang saya gunakan dalam beberapa kode di mana saya tahu tidak ada kunci duplikat. Saya masih dalam pengujian, jadi saya ingin tahu sekarang jika saya mengabaikan sesuatu, daripada mencari tahu nanti.

codingatty
sumber
4
Salah satu imo solusi terbersih dan paling mudah dibaca dan juga menghindari ToList () yang banyak digunakan orang lain.
404
15

Berikut ini berfungsi untuk saya. Jika ada duplikat, itu akan menggunakan nilai dictA.

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]);
}
Ethan Reesor
sumber
@EsbenSkovPedersen Saya tidak berpikir saya telah menemukan itu ketika saya menjawab ini (C # dev baru)
Ethan Reesor
Saya harus menghapus 'where TValue: class' untuk menggunakan Guid sebagai nilai
om471987
@ om471987 Ya, Guid adalah struct bukan kelas, jadi itu masuk akal. Saya pikir pada awalnya saya memiliki beberapa masalah ketika saya tidak menambahkan klausa itu. Tidak ingat kenapa lagi.
Ethan Reesor
10

Berikut adalah fungsi pembantu yang saya gunakan:

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);
        }
    }
}
Andrew Harry
sumber
Ada banyak solusi hebat yang tercantum di sini, tapi saya lebih suka kesederhanaan yang paling ini. Hanya preferensi pribadi saya.
jason
3
if(first == null)maka logika ini tidak berguna karena firsttidak refdan tidak dikembalikan. Dan itu tidak mungkin refkarena itu dinyatakan sebagai contoh antarmuka; Anda harus menghapus pemeriksaan kesalahan atau menyatakannya sebagai instance kelas atau hanya mengembalikan kamus baru (tanpa metode ekstensi).
Grault
@Jesdisciple - menghapus masalah sesuai saran Anda
Andrew Harry
8

Opsi 1: Ini tergantung pada apa yang Anda inginkan terjadi jika Anda yakin bahwa Anda tidak memiliki kunci duplikat di kedua kamus. daripada yang bisa Anda lakukan:

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

Catatan: Ini akan menimbulkan kesalahan jika Anda mendapatkan kunci duplikat dalam kamus.

Opsi 2: Jika Anda dapat memiliki kunci duplikat maka Anda harus menangani kunci duplikat dengan penggunaan klausa where.

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

Catatan: Itu tidak akan mendapatkan kunci duplikat. jika akan ada kunci duplikat daripada itu akan mendapatkan kunci dictionary1.

Opsi 3: Jika Anda ingin menggunakan ToLookup. maka Anda akan mendapatkan pencarian yang dapat memiliki beberapa nilai per kunci. Anda dapat mengonversi pencarian itu ke kamus:

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

sumber
6

Bagaimana kalau menambahkan paramskelebihan?

Anda juga harus mengetikkannya IDictionaryuntuk fleksibilitas maksimum.

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);
}
Bryan Watts
sumber
4
Dia agak menambahkan jawaban lain di sini dengan mencatat Anda bisa membuat kelas DictionaryExtensions lebih bagus. Mungkin pertanyaan ini harus menjadi wiki, atau sebuah kelas yang check-in untuk git ....
Chris Moschini
5

Mempertimbangkan kinerja pencarian kunci kamus dan menghapus karena mereka operasi hash, dan mempertimbangkan kata-kata dari pertanyaan itu adalah cara terbaik , saya pikir di bawah ini adalah pendekatan yang benar-benar valid, dan yang lain agak terlalu rumit, 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);
        }
    }

ATAU jika Anda bekerja di aplikasi multithreaded dan kamus Anda tetap aman utas, Anda harus melakukan ini:

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

Anda kemudian dapat membungkus ini untuk membuatnya menangani enumerasi kamus. Apapun, Anda sedang melihat ~ O (3n) (semua kondisi menjadi sempurna), karena .Add()akan melakukan tambahan, tidak perlu tetapi praktis gratis,Contains() belakang layar. Saya tidak berpikir itu menjadi lebih baik.

Jika Anda ingin membatasi operasi tambahan pada koleksi besar, Anda harus meringkas Countsetiap kamus yang akan Anda gabungkan dan atur kapasitas kamus target menjadi itu, yang menghindari biaya pengubahan ukuran nanti. Jadi, produk akhir adalah sesuatu seperti ini ...

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

Perhatikan bahwa saya menggunakan IList<T>metode ini ... sebagian besar karena jika Anda mengambil dalam IEnumerable<T>, Anda telah membuka diri untuk beberapa enumerasi dari set yang sama, yang bisa sangat mahal jika Anda mendapat koleksi kamus dari LINQ yang ditangguhkan pernyataan.

GoldPaintedLemons
sumber
4

Berdasarkan jawaban di atas, tetapi menambahkan Func-parameter untuk membiarkan pemanggil menangani duplikat:

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));
}
toong
sumber
4

Pesta ini sudah hampir mati sekarang, tapi di sini ada versi "perbaikan" dari user166390 yang memasuki perpustakaan ekstensi saya. Terlepas dari beberapa detail, saya menambahkan delegasi untuk menghitung nilai gabungan.

/// <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;
}
gxtaillon
sumber
2

@Tim: Seharusnya komentar, tetapi komentar tidak memungkinkan untuk mengedit kode.

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

Catatan: Saya menerapkan modifikasi oleh @ANeves ke solusi oleh @Andrew Orsich, sehingga MergeLeft terlihat seperti ini sekarang:

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;
    }
keni
sumber
Dekat dengan apa yang saya tulis sendiri. Ini, ofc, hanya akan bekerja untuk Dictionarytipe. Kelebihan dapat ditambahkan untuk jenis Kamus lain ( ConcurrentDictionary,, ReadOnlyDictionarydll) Saya tidak berpikir pembuatan daftar baru, dan konser diperlukan secara pribadi. Saya hanya mengulangi array params terlebih dahulu, lalu mengulangi setiap KVP dari setiap kamus. Saya tidak melihat hasil imbang.
naksir
Anda dapat menggunakan IDictionary sebagai ganti Kamus
Mariusz Jamro
Anda akan selalu mendapatkan Dictionaryobjek bahkan jika Anda menggunakan metode ekstensi pada a ConcurrentDictionary. Itu bisa menyebabkan sulit untuk melacak bug.
Marcel
2

Saya tahu ini adalah pertanyaan lama, tetapi karena sekarang kami memiliki LINQ, Anda dapat melakukannya dalam satu baris seperti ini

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

atau

mergee.ToList().ForEach(kvp => merged.Append(kvp));
Cruces
sumber
Maaf untuk downvote @Cruces, tetapi contoh pertama Anda duplikat menjawab stackoverflow.com/a/6695211/704808 dan contoh kedua Anda terus membuang IEnumerable <KeyValuePair <string, string >> yang ditambahkan setelah menambahkan setiap item dari mergee.
Bendung,
2

Takut melihat jawaban yang rumit, baru mengenal C #.

Inilah beberapa jawaban sederhana.
Menggabungkan d1, d2, dan sebagainya .. kamus dan menangani kunci yang tumpang tindih ("b" dalam contoh di bawah ini):

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

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

Untuk skenario yang lebih kompleks, lihat jawaban lain.
Harapan itu membantu.

Manohar Reddy Poreddy
sumber
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; });
}

Anda dapat melewati / mengabaikan (default) atau menimpa duplikat: Dan Bob paman Anda asalkan Anda tidak terlalu cerewet tentang kinerja Linq tetapi lebih memilih kode dikelola singkat seperti yang saya lakukan: dalam hal ini Anda dapat menghapus MergeKind.SkipDuplicates default untuk menegakkan pilihan untuk penelepon dan buat pengembang tahu apa hasilnya nanti!

mattjs
sumber
Jawaban halus di bawah tanpa biner enum yang tidak perlu ketika bool akan melakukan ...
mattjs
2

Perhatikan bahwa jika Anda menggunakan metode ekstensi yang disebut 'Tambah', Anda bisa menggunakan inisialisasi koleksi untuk menggabungkan sebanyak mungkin kamus seperti ini:

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" }
};
Andy
sumber
1

Penggabungan menggunakan metode ekstensi. Itu tidak membuang pengecualian ketika ada kunci duplikat, tetapi mengganti kunci-kunci itu dengan kunci dari kamus kedua.

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

Pemakaian:

Dictionary<string, string> merged = first.Merge(second);
Andrew Mikhailov
sumber
1

Disederhanakan dari penggunaan dibandingkan dengan jawaban saya sebelumnya dengan default bool dari gabungan non-destruktif jika ada atau ditimpa seluruhnya jika benar daripada menggunakan enum. Itu masih sesuai dengan kebutuhan saya sendiri tanpa ada kode yang lebih menarik:

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;
        });
    }
}
mattjs
sumber
0

Menggabungkan menggunakan EqualityCompareritem peta untuk perbandingan dengan nilai / tipe yang berbeda. Di sini kita akan memetakan dari KeyValuePair(jenis item ketika menghitung kamus) ke 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();
    }
}

Pemakaian:

// 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);
BSharp
sumber
0

atau :

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

hasilnya adalah gabungan tempat entri duplikat "y" menang.

jtroconisa
sumber
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;
        }
mattylantz
sumber
0

Versi dari @ user166390 menjawab dengan IEqualityComparerparameter yang ditambahkan untuk memungkinkan perbandingan kunci yang tidak sensitif huruf.

    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;
    }
gorillapower
sumber
0
fromDic.ToList().ForEach(x =>
        {
            if (toDic.ContainsKey(x.Key))
                toDic.Remove(x.Key);
            toDic.Add(x);
        });
softwarevamp
sumber