Saya perlu menggabungkan beberapa kamus, berikut ini contohnya:
dict1 = {1:{"a":{A}}, 2:{"b":{B}}}
dict2 = {2:{"c":{C}}, 3:{"d":{D}}
Dengan A
B
C
dan D
menjadi daun pohon, suka{"info1":"value", "info2":"value2"}
Ada tingkat (kedalaman) kamus yang tidak diketahui, bisa jadi {2:{"c":{"z":{"y":{C}}}}}
Dalam kasus saya ini merupakan struktur direktori / file dengan node menjadi dokumen dan meninggalkan file.
Saya ingin menggabungkan mereka untuk memperoleh:
dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}
Saya tidak yakin bagaimana saya bisa melakukannya dengan mudah dengan Python.
python
dictionary
merge
array-merge
fdhex
sumber
sumber
y
diratakan kec
tingkat atau apa? Contoh Anda tidak lengkap.Jawaban:
ini sebenarnya cukup rumit - terutama jika Anda ingin pesan kesalahan yang berguna ketika ada hal-hal yang tidak konsisten, sementara menerima entri duplikat tapi konsisten dengan benar (sesuatu yang tidak ada jawaban lain di sini ....)
dengan asumsi Anda tidak memiliki banyak entri, fungsi rekursif paling mudah:
perhatikan bahwa ini bermutasi
a
- isib
ditambahkana
(yang juga dikembalikan). jika Anda ingin tetapa
Anda bisa menyebutnya sepertimerge(dict(a), b)
.agf menunjukkan (di bawah) bahwa Anda mungkin memiliki lebih dari dua dikt, dalam hal ini Anda dapat menggunakan:
di mana semuanya akan ditambahkan ke dict1.
[note - saya mengedit jawaban awal saya untuk mengubah argumen pertama; yang membuat "mengurangi" lebih mudah dijelaskan]
ps di python 3, Anda juga perlu
from functools import reduce
sumber
reduce
atau loop yang setara untuk bekerja dengan angkadict
s sembarang dari dua. Namun, saya tidak yakin ini melakukan apa yang dia inginkan (dia tidak jelas), Anda berakhir dengan2: {'c': {'z': {'y': {'info1': 'value', 'info2': 'value2'}}}, 'b': {'info1': 'value', 'info2': 'value2'}}
contoh kedua, saya tidak yakin apakah dia inginz
dany
diratakan atau tidak?a[key] = a[key] + b[key]
. Terima kasih atas jawabannya.copy.deepcopy
.Berikut cara mudah untuk melakukannya menggunakan generator:
Ini mencetak:
sumber
Salah satu masalah dengan pertanyaan ini adalah bahwa nilai-nilai dict dapat berupa data yang kompleks dan sewenang-wenang. Berdasarkan jawaban ini dan lainnya, saya menemukan kode ini:
Kasus penggunaan saya menggabungkan file YAML di mana saya hanya harus berurusan dengan subset dari tipe data yang mungkin. Karenanya saya dapat mengabaikan tupel dan objek lainnya. Bagi saya logika penggabungan yang masuk akal artinya
Segala sesuatu yang lain dan yang tak terduga menghasilkan kesalahan.
sumber
isinstance(a, (str, unicode, int, long, float))
bukan?Karena ini adalah pertanyaan kanonik (terlepas dari non-generalisasi tertentu) saya menyediakan pendekatan Pythonic kanonik untuk menyelesaikan masalah ini.
Kasus paling sederhana: "daun adalah dict bersarang yang berakhir dict kosong":
Ini adalah kasus paling sederhana untuk rekursi, dan saya akan merekomendasikan dua pendekatan naif:
Saya yakin saya lebih suka yang kedua daripada yang pertama, tetapi perlu diingat bahwa keadaan awal dari yang pertama harus dibangun kembali dari asalnya. Inilah penggunaannya:
Kasing Kompleks: "daun dari jenis lain:"
Jadi jika mereka berakhir dicts, itu adalah kasus sederhana penggabungan akhir dicts. Jika tidak, itu tidak sepele. Jika string, bagaimana Anda menggabungkannya? Set dapat diperbarui dengan cara yang sama, sehingga kami dapat memberikan perawatan itu, tetapi kami kehilangan urutan penggabungannya. Jadi apakah pesanan itu penting?
Jadi sebagai pengganti informasi lebih lanjut, pendekatan yang paling sederhana adalah memberi mereka perlakuan pembaruan standar jika kedua nilai bukan dicts: yaitu nilai dict kedua akan menimpa yang pertama, bahkan jika nilai dict kedua adalah Tidak ada dan nilai pertama adalah dict dengan banyak info.
Dan sekarang
kembali
Aplikasi untuk pertanyaan awal:
Saya harus menghapus kurung kurawal di sekitar huruf dan menempatkannya dalam tanda kutip tunggal untuk ini menjadi Python yang sah (kalau tidak mereka akan menetapkan literal dalam Python 2.7+) serta menambahkan kurung kurawal yang hilang:
dan
rec_merge(dict1, dict2)
sekarang kembali:Yang cocok dengan hasil yang diinginkan dari pertanyaan asli (setelah mengubah, misalnya
{A}
ke'A'
.)sumber
Berdasarkan cooke @andrew. Versi ini menangani daftar dikte bersarang dan juga memungkinkan opsi untuk memperbarui nilai
sumber
Prosedur rekursif sederhana ini akan menggabungkan satu kamus ke kamus lain sambil meng-override kunci yang saling bertentangan:
Keluaran:
sumber
Berdasarkan jawaban dari @andrew cooke. Ini mengurus daftar bersarang dengan cara yang lebih baik.
sumber
Jika Anda memiliki tingkat kamus yang tidak diketahui, maka saya menyarankan fungsi rekursif:
sumber
Gambaran
Pendekatan berikut membagi masalah penggabungan dalam dikt ke dalam:
Fungsi gabungan dangkal yang diparameterisasi
merge(f)(a,b)
yang menggunakan fungsif
untuk menggabungkan dua dictsa
danb
Fungsi merger rekursif
f
untuk digunakan bersamamerge
Penerapan
Fungsi untuk menggabungkan dua dikte (tidak bersarang) dapat ditulis dalam banyak cara. Saya pribadi suka
Cara yang baik untuk mendefinisikan fungsi merger rekursif yang tepat
f
adalah dengan menggunakan multipledispatch yang memungkinkan untuk mendefinisikan fungsi yang mengevaluasi sepanjang jalur yang berbeda tergantung pada jenis argumen mereka.Contoh
Untuk menggabungkan dua dikte bersarang cukup gunakan
merge(f)
misalnya:Catatan:
Keuntungan dari pendekatan ini adalah:
Fungsi ini dibangun dari fungsi yang lebih kecil yang masing-masing melakukan satu hal yang membuat kode lebih mudah untuk dipertimbangkan dan diuji
Perilaku ini bukan hard-coded tetapi dapat diubah dan diperluas sesuai kebutuhan yang meningkatkan penggunaan kembali kode (lihat contoh di bawah).
Kustomisasi
Beberapa jawaban juga dianggap sebagai dikte yang berisi daftar, misalnya dikte lain (yang berpotensi bersarang). Dalam hal ini orang mungkin ingin memetakan daftar dan menggabungkannya berdasarkan posisi. Ini dapat dilakukan dengan menambahkan definisi lain ke fungsi merger
f
:sumber
Jika seseorang menginginkan yang lain pendekatan untuk masalah ini, inilah solusi saya.
Kebajikan : pendek, deklaratif, dan fungsional dalam gaya (rekursif, tidak ada mutasi).
Kerugian Potensial : Ini mungkin bukan gabungan yang Anda cari. Konsultasikan dengan docstring untuk semantik.
sumber
Anda dapat mencoba mergedeep .
Instalasi
Pemakaian
sumber
Ada sedikit masalah dengan andrew cookes menjawab: Dalam beberapa kasus ini memodifikasi argumen kedua
b
ketika Anda memodifikasi dict yang dikembalikan. Khususnya karena baris ini:Jika
b[key]
adict
, itu hanya akan ditugaskana
, yang berarti setiap modifikasi berikutnya yangdict
akan mempengaruhi keduanyaa
danb
.Untuk memperbaiki ini, saluran harus diganti dengan ini:
Dimana
clone_dict
:Masih. Ini jelas tidak memperhitungkan
list
,set
dan hal-hal lain, tapi saya harap ini menggambarkan perangkap ketika mencoba untuk bergabungdicts
.Dan demi kelengkapannya, ini adalah versi saya, di mana Anda dapat memberikannya beberapa
dicts
:sumber
deepcopy
bukanclone_dict
?Versi fungsi ini akan menjelaskan N jumlah kamus, dan hanya kamus - tidak ada parameter yang tidak patut yang dapat dilewati, atau akan meningkatkan TypeError. Menggabungkan itu sendiri bertanggung jawab atas konflik utama, dan alih-alih menimpa data dari kamus lebih jauh ke bawah rantai gabungan, itu menciptakan serangkaian nilai dan menambahkannya; tidak ada data yang hilang.
Ini mungkin bukan yang paling efisien di halaman, tapi itu yang paling menyeluruh dan Anda tidak akan kehilangan informasi apa pun ketika Anda menggabungkan 2 ke N Anda.
output: {1: [1, 2], 2: {1: 2, 3: 1}, 4: 4}
sumber
Karena dictviews mendukung operasi yang ditetapkan, saya dapat sangat menyederhanakan jawaban jterrace.
Setiap upaya untuk menggabungkan dict dengan non dict (secara teknis, objek dengan metode 'kunci' dan objek tanpa metode 'kunci') akan meningkatkan AttributeError. Ini termasuk panggilan awal ke fungsi dan panggilan rekursif. Ini persis apa yang saya inginkan jadi saya meninggalkannya. Anda dapat dengan mudah menangkap AttributeErrors yang dilemparkan oleh panggilan rekursif dan kemudian menghasilkan nilai apa pun yang Anda inginkan.
sumber
Pendek-n-manis:
Ini berfungsi seperti (dan sedang dibangun di atas)
dict.update
metode Python . Itu kembaliNone
(Anda selalu dapat menambahkanreturn d
jika Anda mau) karena pembaruan dictd
di tempat. Kunci dalamv
akan menimpa setiap kunci yang ada did
(itu tidak mencoba untuk menafsirkan konten dict).Ini juga akan berfungsi untuk pemetaan ("dict-like") lainnya.
sumber
Kode akan bergantung pada aturan Anda untuk menyelesaikan konflik gabungan, tentu saja. Berikut adalah versi yang dapat mengambil sejumlah argumen arbitrer dan menggabungkannya secara rekursif ke kedalaman arbitrer, tanpa menggunakan mutasi objek apa pun. Itu menggunakan aturan berikut untuk menyelesaikan konflik gabungan:
{"foo": {...}}
diutamakan{"foo": "bar"}
){"a": 1}
,{"a", 2}
dan{"a": 3}
dalam rangka, hasilnya akan{"a": 3}
)sumber
Saya memiliki dua kamus (
a
danb
) yang masing-masing dapat berisi sejumlah kamus bersarang. Saya ingin menggabungkan mereka secara rekursif, denganb
mengambil alih prioritasa
.Mempertimbangkan kamus bersarang sebagai pohon, yang saya inginkan adalah:
a
sehingga setiap jalur ke setiap daunb
akan diwakili dalama
a
jika daun ditemukan di jalur yang sesuai dib
b
simpul daun tetap daun.Jawaban yang ada agak rumit untuk seleraku dan meninggalkan beberapa detail di rak. Saya meretas bersama-sama berikut ini, yang lulus tes unit untuk set data saya.
Contoh (diformat untuk kejelasan):
Jalur
b
yang perlu dipertahankan adalah:1 -> 'b' -> 'white'
2 -> 'd' -> 'black'
3 -> 'e'
.a
memiliki jalur unik dan tidak bertentangan:1 -> 'a' -> 'red'
1 -> 'c' -> 'orange' -> 'dog'
jadi mereka masih terwakili di peta gabungan.
sumber
Saya punya solusi berulang - bekerja jauh lebih baik dengan dikte besar & banyak di antaranya (misalnya jsons dll):
perhatikan bahwa ini akan menggunakan nilai dalam d2 untuk mengesampingkan d1, jika keduanya bukan keduanya. (Sama seperti python
dict.update()
)beberapa tes:
Saya telah menguji dengan sekitar ~ 1200 dicts - metode ini memakan waktu 0,4 detik, sedangkan solusi rekursif mengambil ~ 2,5 detik.
sumber
Ini akan membantu menggabungkan semua item
dict2
menjadidict1
:Silakan mengujinya dan beri tahu kami apakah ini yang Anda inginkan.
EDIT:
Solusi yang disebutkan di atas hanya menggabungkan satu level, tetapi dengan benar memecahkan contoh yang diberikan oleh OP. Untuk menggabungkan beberapa level, rekursi harus digunakan.
sumber
for k,v in dict2.iteritems(): dict1.setdefault(k,{}).update(v)
. Tapi seperti yang ditunjukkan @agf, ini tidak menggabungkan dicts yang bersarang.{'a':'b'}
dengan{'a':{'c':'d'}
).Saya telah menguji solusi Anda dan memutuskan untuk menggunakan yang ini dalam proyek saya:
Melewati fungsi sebagai parameter adalah kunci untuk memperluas solusi jterrace agar berperilaku seperti semua solusi rekursif lainnya.
sumber
Cara termudah yang bisa saya pikirkan adalah:
Keluaran:
sumber
Saya punya solusi lain yang sedikit berbeda di sini:
Secara default ini menyelesaikan konflik yang mendukung nilai-nilai dari dikt kedua, tetapi Anda dapat dengan mudah menimpanya, dengan beberapa sihir Anda bahkan dapat membuang pengecualian darinya. :)
sumber
sumber
hei di sana saya juga punya masalah yang sama tetapi saya memikirkan solusi dan saya akan mempostingnya di sini, kalau-kalau ini juga berguna untuk yang lain, pada dasarnya menggabungkan kamus yang bersarang dan juga menambahkan nilai-nilai, bagi saya saya perlu menghitung beberapa probabilitas jadi ini satu bekerja dengan baik:
dengan menggunakan metode di atas kita dapat menggabungkan:
target = {'6,6': {'6,63': 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 1} , '6,63': {'63, 4 ': 1}}
src = {'5,4': {'4,4': 1}, '5,5': {'5,4': 1}, '4,4': {'4,3': 1} }
dan ini akan menjadi: {'5,5': {'5,4': 1}, '5,4': {'4,4': 1}, '6,6': {'6,63' : 1}, '63, 4 ': {' 4,4 ': 1},' 4,4 ': {' 4,3 ': 2},' 6,63 ': {'63, 4': 1 }}
perhatikan juga perubahan di sini:
target = {'6,6': {'6,63': 1}, '6,63': {'63, 4 ': 1}, ' 4,4 ': {' 4,3 ': 1} , '63, 4 ': {' 4,4 ': 1}}
src = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '4,4': {'4,9': 1} , '3,4': {'4,4': 1}, '5,5': {'5,4': 1}}
menggabungkan = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '6,63': {'63, 4 ': 1} , '5,5': {'5,4': 1}, '6,6': {'6,63': 1}, '3,4': {'4,4': 1}, ' 63,4 ': {' 4,4 ': 1}, ' 4,4 ': {' 4,3 ': 1,' 4,9 ': 1} }
jangan lupa juga menambahkan impor untuk salinan:
sumber
Keluaran:
sumber
lihat
toolz
paketnyamemberi
sumber
Fungsi berikut menggabungkan b menjadi a.
sumber
Dan hanya sedikit variasi lain:
Berikut ini adalah fungsi pembaruan mendalam set python3 murni. Ini memperbarui kamus bersarang dengan perulangan melalui satu tingkat pada satu waktu dan menyebut dirinya untuk memperbarui setiap tingkat nilai kamus berikutnya:
Contoh sederhana:
sumber
Bagaimana dengan jawaban lain?!? Yang ini juga menghindari mutasi / efek samping:
sumber