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.
c#
dictionary
merge
orip
sumber
sumber
dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value)
.Jawaban:
Ini sebagian tergantung pada apa yang Anda inginkan terjadi jika Anda mengalami duplikat. Misalnya, Anda dapat melakukan:
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:
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.
sumber
GroupBy
memang bisa sedikit lebih efisien - tapi saya ragu itu akan menjadi signifikan.ToDictionary
pemanggilan 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.Saya akan melakukannya seperti ini:
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.
sumber
dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value)
. Dengan cara ini nilai daridictionaryFrom
menimpa nilai untuk kunci yang mungkin ada.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):
sumber
this T me
kamus dinyatakan menggunakannew Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)
atau sesuatu yang serupa, kamus yang dihasilkan tidak akan mempertahankan perilaku yang sama.me
sebuahDictionary<K,V>
, Anda kemudian dapat melakukanvar newMap = new Dictionary<TKey, TValue>(me, me.Comparer);
, dan Anda juga menghindari tambahanT
parameter tipe.Solusi sepele adalah:
sumber
Coba yang berikut ini
sumber
sumber
foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))
Menggunakannya dalam kode produksi, jika ada kelemahan silakan beri tahu saya! :)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.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)?
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.
sumber
Berikut ini berfungsi untuk saya. Jika ada duplikat, itu akan menggunakan nilai dictA.
sumber
Berikut adalah fungsi pembantu yang saya gunakan:
sumber
if(first == null)
maka logika ini tidak berguna karenafirst
tidakref
dan tidak dikembalikan. Dan itu tidak mungkinref
karena itu dinyatakan sebagai contoh antarmuka; Anda harus menghapus pemeriksaan kesalahan atau menyatakannya sebagai instance kelas atau hanya mengembalikan kamus baru (tanpa metode ekstensi).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:
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.
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:
sumber
Bagaimana kalau menambahkan
params
kelebihan?Anda juga harus mengetikkannya
IDictionary
untuk fleksibilitas maksimum.sumber
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.
ATAU jika Anda bekerja di aplikasi multithreaded dan kamus Anda tetap aman utas, Anda harus melakukan ini:
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
Count
setiap 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 ...Perhatikan bahwa saya menggunakan
IList<T>
metode ini ... sebagian besar karena jika Anda mengambil dalamIEnumerable<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.sumber
Berdasarkan jawaban di atas, tetapi menambahkan Func-parameter untuk membiarkan pemanggil menangani duplikat:
sumber
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.
sumber
@Tim: Seharusnya komentar, tetapi komentar tidak memungkinkan untuk mengedit kode.
Catatan: Saya menerapkan modifikasi oleh @ANeves ke solusi oleh @Andrew Orsich, sehingga MergeLeft terlihat seperti ini sekarang:
sumber
Dictionary
tipe. Kelebihan dapat ditambahkan untuk jenis Kamus lain (ConcurrentDictionary
,,ReadOnlyDictionary
dll) 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.Dictionary
objek bahkan jika Anda menggunakan metode ekstensi pada aConcurrentDictionary
. Itu bisa menyebabkan sulit untuk melacak bug.Saya tahu ini adalah pertanyaan lama, tetapi karena sekarang kami memiliki LINQ, Anda dapat melakukannya dalam satu baris seperti ini
atau
sumber
mergee
.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
Contoh 2
Untuk skenario yang lebih kompleks, lihat jawaban lain.
Harapan itu membantu.
sumber
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!
sumber
Perhatikan bahwa jika Anda menggunakan metode ekstensi yang disebut 'Tambah', Anda bisa menggunakan inisialisasi koleksi untuk menggabungkan sebanyak mungkin kamus seperti ini:
sumber
Penggabungan menggunakan metode ekstensi. Itu tidak membuang pengecualian ketika ada kunci duplikat, tetapi mengganti kunci-kunci itu dengan kunci dari kamus kedua.
Pemakaian:
sumber
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:
sumber
Menggabungkan menggunakan
EqualityComparer
item peta untuk perbandingan dengan nilai / tipe yang berbeda. Di sini kita akan memetakan dariKeyValuePair
(jenis item ketika menghitung kamus) keKey
.Pemakaian:
sumber
atau :
hasilnya adalah gabungan tempat entri duplikat "y" menang.
sumber
sumber
Versi dari @ user166390 menjawab dengan
IEqualityComparer
parameter yang ditambahkan untuk memungkinkan perbandingan kunci yang tidak sensitif huruf.sumber
sumber