Birden çok dikteyi aynı anahtarla nasıl birleştirebilirim?


88

Bunun gibi birden çok diktim / anahtar / değer çiftim var:

d1 = {key1: x1, key2: y1}  
d2 = {key1: x2, key2: y2}  

Sonucun yeni bir dikte olmasını istiyorum (mümkünse en verimli şekilde):

d = {key1: (x1, x2), key2: (y1, y2)}  

Aslında d sonucunun şöyle olmasını istiyorum:

d = {key1: (x1.x1attrib, x2.x2attrib), key2: (y1.y1attrib, y2.y2attrib)}  

Biri bana ilk sonucu nasıl alacağımı gösterirse, gerisini çözebilirim.


4
@Salil: Her bir anahtarın tüm sözlüklerde mevcut olduğunu varsayabilir miyiz?
Björn Pollex


Merhaba Space_C0wb0y, evet, anahtarlar tüm sözlüklerde mevcuttur.
Salil

Tüm diktelerin aynı anahtarlara sahip olup olmadığını belirlemek kesinlikle çok önemlidir.
yugr

Yanıtlar:


46

tüm anahtarların her zaman tüm dicts'te mevcut olduğunu varsayarak:

ds = [d1, d2]
d = {}
for k in d1.iterkeys():
    d[k] = tuple(d[k] for d in ds)

Not: Python 3.x'te aşağıdaki kodu kullanın:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = tuple(d[k] for d in ds)

ve dic uyuşmuş diziler içeriyorsa:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = np.concatenate(list(d[k] for d in ds))

3
Sadece "d1'de k için" bence.
Salil

ve d.get (k, Hiçbiri) d [k] yerine
tahir

1
@tahir Bu, diktlerin eşleşmeyen anahtarlara sahip olduğu anlamına gelir, bu nedenle üzerinde yineleme d1doğru değildir (diğer sözcüklerdeki anahtarları gözden kaçırabilir).
yugr

1
Python 3 kullanıcıları için: d1.iterkeys () = d1.items ()
Riley

Python3.x'te hala benim için çalışmıyor. Değerlerim diziler olmasa bile bunu denedim ve çalışıyor. Bununla birlikte, çıktı değerleri diziler olacaktır. stackoverflow.com/questions/54040858/…
Ric S

74

Anahtarların yalnızca bazı sözlüklerde bulunduğu durumlarda, keyfi miktarda sözlüğü işleyecek genel bir çözüm:

from collections import defaultdict

d1 = {1: 2, 3: 4}
d2 = {1: 6, 3: 7}

dd = defaultdict(list)

for d in (d1, d2): # you can list as many input dicts as you want here
    for key, value in d.items():
        dd[key].append(value)

print(dd)

Şovlar:

defaultdict(<type 'list'>, {1: [2, 6], 3: [4, 7]})

Ayrıca, almak için .attribşu şekilde değiştirin append(value):append(value.attrib)


Bence OP, değerleri tupleistemiyor list.
user225312

1
@AA: gerçekten önemli mi? Bazı tuşların her yerde bulunmadığı daha genel çoklu giriş diktleri durumunda tuple oluşturmak daha zor olacak,
imho

1
Daha sonra dict, defaultdictvar dictolmayan anahtarlar vb. İçin normal davranışınız olması için normal bir şey yapmak isteyebilirsiniz : dd = dict(dd)
Ned Deily

@Ned: iyi nokta, ancak verilerin nihai kullanımına bağlı
Eli Bendersky

@Eli: Hayır önemli değil ama sadece OP'nin istediği şeyi temel almaya çalışıyordum ve sizden gelen tuplelar için bir çözüm olmasını umuyordum :-)
user225312

4

Yalnızca d1 ve d2'ye sahipseniz,

from collections import defaultdict

d = defaultdict(list)
for a, b in d1.items() + d2.items():
    d[a].append(b)

4

İşte her iki sözlükte aynı anahtarlar olmasa bile işe yarayacak bir yaklaşım:

d1 = {'a':'test','b':'btest','d':'dreg'}
d2 = {'a':'cool','b':'main','c':'clear'}

d = {}

for key in set(d1.keys() + d2.keys()):
    try:
        d.setdefault(key,[]).append(d1[key])        
    except KeyError:
        pass

    try:
        d.setdefault(key,[]).append(d2[key])          
    except KeyError:
        pass

print d

Bu, aşağıdaki girdiyi oluşturacaktır:

{'a': ['test', 'cool'], 'c': ['clear'], 'b': ['btest', 'main'], 'd': ['dreg']}

Yanıtta set(d1.keys() + d2.keys()) olarak değiştirilebilir set(list(d1.keys()) + list(d2.keys()))(Python 3.x için)? Aksi takdirde TypeError: unsupported operand type(s) for +: 'dict_keys' and 'dict_keys'python3.x
R4444

4
dict1 = {'m': 2, 'n': 4}
dict2 = {'n': 3, 'm': 1}

Anahtarların aynı sırada olduğundan emin olun:

dict2_sorted = {i:dict2[i] for i in dict1.keys()}

keys = dict1.keys()
values = zip(dict1.values(), dict2_sorted.values())
dictionary = dict(zip(keys, values))

verir:

{'m': (2, 1), 'n': (4, 3)}

2
İçindeki öğelerin sırası values()tanımsız olduğundan, ilgisiz anahtarlardan değerleri birleştiriyor olabilirsiniz.
yugr

Şimdi geri bildirimlerinizi yakalayabilmek için değişiklikleri uyguladım
Mahdi Ghelichi

Değişikliğin sorunu çözeceğini sanmıyorum. Öngörülebilir sonuçlar elde etmek için sorted(d.items())veya kullanmanız gerekir sorted(d.keys()).
yugr

Bunun aksini ispatlayan bir örnek verebilir misiniz? dict2_sorted, python'da sıralanmış bir sözlüktür!
Mahdi Ghelichi

1
Bununla ilgili küçük bir araştırma yaptım. Python'un (3.6+) son sürümlerinde yineleme sırası, ekleme sırasıyla eşleşmeye başladı (örneğin, buraya bakın ), bu da kodunuzun geçmesini sağlar. Ancak bu, güvenilmemesi gereken bir uygulama detayı olarak kabul edilir. İkinci örneğim ( buraya bakın ) eski Python 3.4 kullanan onlinegdb'de güvenilir bir şekilde başarısız oluyor . Diğer çevrimiçi tercümanlar daha yeni Python kullanır, bu nedenle sorun orada çoğaltılamaz.
yugr

2

Bu işlev, iki sözlükteki tuşlar farklı olsa bile iki dikteyi birleştirir:

def combine_dict(d1, d2):
    combined = {}
    for k in set(d1.keys()) | set(d2.keys()):
        combined[k] = tuple(d[k] for d in [d1, d2] if k in d)
    return combined

Misal:

d1 = {
    'a': 1,
    'b': 2,
}
d2` = {
    'b': 'boat',
    'c': 'car',
}
combine_dict(d1, d2)
# Returns: {
#    'a': (1,),
#    'b': (2, 'boat'),
#    'c': ('car',)
# }

1

Python 3.x Güncellemesi

Eli Bendersky cevabından:

Python 3 kaldırılan dict.iteritems yerine dict.items kullanır. Python wiki'ye bakın: https://wiki.python.org/moin/Python3.0

from collections import defaultdict

dd = defaultdict(list)

for d in (d1, d2):
    for key, value in d.items():
        dd[key].append(value)

1

TÜM anahtarların listesine sahip olduğunuzu varsayın (bu listeyi tüm sözlükleri yineleyerek ve anahtarlarını alarak alabilirsiniz). Adını verelim listKeys. Ayrıca:

  • listValues , birleştirmek istediğiniz tek bir anahtar için TÜM değerlerin listesidir.
  • allDicts: birleştirmek istediğiniz tüm sözlükler.
result = {}
for k in listKeys:
    listValues = [] #we will convert it to tuple later, if you want.
    for d in allDicts:
       try:
            fileList.append(d[k]) #try to append more values to a single key
        except:
            pass
    if listValues: #if it is not empty
        result[k] = typle(listValues) #convert to tuple, add to new dictionary with key k

0
def merge(d1, d2, merge):
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge(result[k], v)
        else:
            result[k] = v
    return result

d1 = {'a': 1, 'b': 2}
d2 = {'a': 1, 'b': 3, 'c': 2}
print merge(d1, d2, lambda x, y:(x,y))

{'a': (1, 1), 'c': 2, 'b': (2, 3)}

0

İki listeli çözümleri tamamlamak için, işte tek bir listeyi işlemek için bir çözüm .

Örnek bir liste (NetworkX ile ilgili; okunabilirlik için burada manuel olarak biçimlendirilmiştir):

ec_num_list = [((src, tgt), ec_num['ec_num']) for src, tgt, ec_num in G.edges(data=True)]

print('\nec_num_list:\n{}'.format(ec_num_list))
ec_num_list:
[((82, 433), '1.1.1.1'),
  ((82, 433), '1.1.1.2'),
  ((22, 182), '1.1.1.27'),
  ((22, 3785), '1.2.4.1'),
  ((22, 36), '6.4.1.1'),
  ((145, 36), '1.1.1.37'),
  ((36, 154), '2.3.3.1'),
  ((36, 154), '2.3.3.8'),
  ((36, 72), '4.1.1.32'),
  ...] 

Aynı kenarlar için yinelenen değerleri not edin (başlıklar tarafından tanımlanan). Bu "değerleri" karşılık gelen "anahtarlara" harmanlamak için:

from collections import defaultdict
ec_num_collection = defaultdict(list)
for k, v in ec_num_list:
    ec_num_collection[k].append(v)

print('\nec_num_collection:\n{}'.format(ec_num_collection.items()))
ec_num_collection:
[((82, 433), ['1.1.1.1', '1.1.1.2']),   ## << grouped "values"
((22, 182), ['1.1.1.27']),
((22, 3785), ['1.2.4.1']),
((22, 36), ['6.4.1.1']),
((145, 36), ['1.1.1.37']),
((36, 154), ['2.3.3.1', '2.3.3.8']),    ## << grouped "values"
((36, 72), ['4.1.1.32']),
...] 

Gerekirse, bu listeyi dikteye dönüştürün:

ec_num_collection_dict = {k:v for k, v in zip(ec_num_collection, ec_num_collection)}

print('\nec_num_collection_dict:\n{}'.format(dict(ec_num_collection)))
  ec_num_collection_dict:
  {(82, 433): ['1.1.1.1', '1.1.1.2'],
  (22, 182): ['1.1.1.27'],
  (22, 3785): ['1.2.4.1'],
  (22, 36): ['6.4.1.1'],
  (145, 36): ['1.1.1.37'],
  (36, 154): ['2.3.3.1', '2.3.3.8'],
  (36, 72): ['4.1.1.32'],
  ...}

Referanslar


0

Blubb cevabından:

Ayrıca, her listeden değerleri kullanarak demeti doğrudan oluşturabilirsiniz.

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = (d1[k], d2[k])

Tupleleriniz için belirli bir sıralamanız varsa bu yararlı olabilir.

ds = [d1, d2, d3, d4]
d = {}
for k in d1.keys():
  d[k] = (d3[k], d1[k], d4[k], d2[k]) #if you wanted tuple in order of d3, d1, d4, d2

0

Bu kütüphane bana yardımcı oldu, aynı ada sahip ancak farklı değerlere sahip iç içe geçmiş anahtarların bir listesi vardı, diğer tüm çözümler bu iç içe geçmiş anahtarları geçersiz kılıyordu.

https://pypi.org/project/deepmerge/

from deepmerge import always_merger

def process_parms(args):
    temp_list = []
    for x in args:
        with open(x, 'r') as stream:
            temp_list.append(yaml.safe_load(stream))

    return always_merger.merge(*temp_list)

0

Anahtarlar iç içe geçmişse:

d1 = { 'key1': { 'nkey1': 'x1' }, 'key2': { 'nkey2': 'y1' } } 
d2 = { 'key1': { 'nkey1': 'x2' }, 'key2': { 'nkey2': 'y2' } }
ds = [d1, d2]
d = {}
for k in d1.keys():
    for k2 in d1[k].keys():
        d.setdefault(k, {})
        d[k].setdefault(k2, [])
        d[k][k2] = tuple(d[k][k2] for d in ds)

verim:

{'key1': {'nkey1': ('x1', 'x2')}, 'key2': {'nkey2': ('y1', 'y2')}}

-4

Kompakt bir olasılık

d1={'a':1,'b':2}
d2={'c':3,'d':4}
context={**d1, **d2}
context
{'b': 2, 'c': 3, 'd': 4, 'a': 1}

soru, diktleri aynı anahtarla birleştirmekle ilgili. gerekli cevap değilsin.
Pbd
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.