Jak połączyć wiele dykt z tym samym kluczem?

88

Mam wiele par dykt / klucz-wartość, takich jak ta:

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

Chcę, aby wynik był nowym dyktatem (w najbardziej efektywny sposób, jeśli to możliwe):

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

Właściwie chcę, aby wynik d był:

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

Jeśli ktoś pokaże mi, jak uzyskać pierwszy wynik, mogę wymyślić resztę.

Salil
źródło
4
@Salil: Czy możemy założyć, że każdy klucz jest obecny we wszystkich słownikach?
Björn Pollex
możliwy duplikat łączenia słowników Pythona
Johnsyweb
Cześć Space_C0wb0y, tak, klucze są obecne we wszystkich słownikach.
Salil
Absolutnie konieczne jest określenie, czy wszystkie dykty mają takie same klucze.
yugr

Odpowiedzi:

46

zakładając, że wszystkie klucze są zawsze obecne we wszystkich dyktach:

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

Uwaga: W Pythonie 3.x użyj poniższego kodu:

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

a jeśli dic zawiera numpy tablice:

ds = [d1, d2]
d = {}
for k in d1.keys():
  d[k] = np.concatenate(list(d[k] for d in ds))
blubb
źródło
3
Myślę, że po prostu „za k w d1”.
Salil
i d.get (k, None) zamiast d [k]
tahir
1
@tahir Oznaczałoby to, że dykty mają niepasujące klucze, więc iteracja d1nie jest poprawna (może brakować kluczy w innych dyktach).
yugr
1
Dla użytkowników Pythona 3: d1.iterkeys () = d1.items ()
Riley
Nadal nie działa dla mnie w Python3.x. Próbowałem tego, nawet jeśli moje wartości nie są tablicami i działa. Jednak wartości wyjściowe będą tablicami. stackoverflow.com/questions/54040858/…
Ric S
74

Oto ogólne rozwiązanie, które będzie obsługiwać dowolną liczbę słowników, z przypadkami, gdy klucze znajdują się tylko w niektórych słownikach:

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)

Przedstawia:

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

Aby uzyskać swój .attrib, po prostu zmień append(value)naappend(value.attrib)

Eli Bendersky
źródło
Myślę, że PO chce wartości jako tuplenie list.
user225312
1
@AA: czy to naprawdę ma znaczenie? krotki będą trudniejsze do zbudowania w bardziej ogólnym przypadku dyktowania z wieloma wejściami, gdzie niektóre klucze nie są obecne wszędzie, imho
Eli Bendersky
1
Możesz wtedy chcieć zrobić normalne dictwyjście, defaultdictaby mieć normalne dictzachowanie dla nieistniejących kluczy itp .: dd = dict(dd)
Ned Deily
@Ned: słuszna uwaga, ale zależy to od ostatecznego wykorzystania danych
Eli Bendersky
@Eli: Nie, to nie ma znaczenia, ale po prostu próbowałem oprzeć to na tym, czego chciał OP i miałem nadzieję, że będzie rozwiązanie dla krotek od ciebie :-)
user225312
4

Jeśli masz tylko d1 i d2,

from collections import defaultdict

d = defaultdict(list)
for a, b in d1.items() + d2.items():
    d[a].append(b)
riza
źródło
4

Oto jedno podejście, którego możesz użyć, które zadziała, nawet jeśli oba słowniki nie mają tych samych kluczy:

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

Spowoduje to wygenerowanie poniższych danych wejściowych:

{'a': ['test', 'cool'], 'c': ['clear'], 'b': ['btest', 'main'], 'd': ['dreg']}
sateesh
źródło
Czy set(d1.keys() + d2.keys()) można zmienić na set(list(d1.keys()) + list(d2.keys()))w odpowiedzi (dla Pythona 3.x)? W przeciwnym razie pojawi się TypeError: unsupported operand type(s) for +: 'dict_keys' and 'dict_keys'błąd w python3.x
R4444,
4
dict1 = {'m': 2, 'n': 4}
dict2 = {'n': 3, 'm': 1}

Upewnij się, że klucze są w tej samej kolejności:

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

daje:

{'m': (2, 1), 'n': (4, 3)}
Mahdi Ghelichi
źródło
2
Kolejność elementów w values()jest niezdefiniowana, więc możesz scalać wartości z niepowiązanych kluczy.
yugr
Właśnie zastosowałem zmiany, aby teraz można było
zebrać
Nie sądzę, aby zmiana rozwiązała problem. Musisz użyć sorted(d.items())lub sorted(d.keys())osiągnąć przewidywalne wyniki.
yugr
Czy możesz podać przykład, który udowodni, że jest inaczej? dict2_sorted to posortowany słownik w Pythonie!
Mahdi Ghelichi
1
Zrobiłem małe badanie tego. W ostatnich wersjach Pythona (3.6+) kolejność iteracji zaczęła odpowiadać kolejności wstawiania (patrz np. Tutaj ), co sprawia, że ​​kod przechodzi. Uważa się to jednak za szczegół implementacji, na którym nie należy polegać. Mój drugi przykład (patrz tutaj ) niezawodnie zawodzi w onlinegdb, który używa starego Pythona 3.4. Inni tłumacze online używają nowszych Pythonów, więc nie można tam odtworzyć problemu.
yugr
2

Ta funkcja łączy dwie dykty, nawet jeśli klucze w dwóch słownikach są różne:

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

Przykład:

d1 = {
    'a': 1,
    'b': 2,
}
d2` = {
    'b': 'boat',
    'c': 'car',
}
combine_dict(d1, d2)
# Returns: {
#    'a': (1,),
#    'b': (2, 'boat'),
#    'c': ('car',)
# }
Strumień
źródło
1

Aktualizacja Pythona 3.x.

Odpowiedź Eli Bendersky'ego:

Python 3 usunął dict.iteritems zamiast tego używa dict.items. Zobacz wiki Pythona: 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)
szczery
źródło
1

Załóżmy, że masz listę WSZYSTKICH kluczy (możesz uzyskać tę listę, przechodząc przez wszystkie słowniki i uzyskując ich klucze). Nazwijmy to listKeys. Również:

  • listValues to lista WSZYSTKICH wartości dla pojedynczego klucza, który chcesz scalić.
  • allDicts: wszystkie słowniki, które chcesz scalić.
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
Długo
źródło
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)}
ralphtheninja
źródło
0

Aby uzupełnić rozwiązania z dwiema listami, oto rozwiązanie do przetwarzania pojedynczej listy.

Przykładowa lista (związana z NetworkX; ręcznie sformatowana tutaj w celu zwiększenia czytelności):

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'),
  ...] 

Zwróć uwagę na zduplikowane wartości dla tych samych krawędzi (zdefiniowanych przez krotki). Aby zestawić te „wartości” z odpowiadającymi im „kluczami”:

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']),
...] 

W razie potrzeby przekonwertuj tę listę na dict:

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'],
  ...}

Bibliografia

Victoria Stuart
źródło
0

Z odpowiedzi blubb:

Możesz również bezpośrednio utworzyć krotkę, używając wartości z każdej listy

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

Może to być przydatne, jeśli masz określoną kolejność krotek

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
mrożone5032
źródło
0

Ta biblioteka mi pomogła, miałem dyktowaną listę zagnieżdżonych kluczy o tej samej nazwie, ale z różnymi wartościami, każde inne rozwiązanie zastępowało te zagnieżdżone klucze.

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)
jmcgrath207
źródło
0

Jeśli klucze są zagnieżdżone:

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)

plony:

{'key1': {'nkey1': ('x1', 'x2')}, 'key2': {'nkey2': ('y1', 'y2')}}
lys
źródło
-4

Kompaktowa możliwość

d1={'a':1,'b':2}
d2={'c':3,'d':4}
context={**d1, **d2}
context
{'b': 2, 'c': 3, 'd': 4, 'a': 1}
user2897775
źródło
pytanie dotyczy łączenia dykt z tym samym kluczem. twoja odpowiedź nie jest wymagana.
Pbd