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ę.


4
@Salil: Czy możemy założyć, że każdy klucz jest obecny we wszystkich słownikach?
Björn Pollex


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

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)


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)

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

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

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',)
# }

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)

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

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

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


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

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)

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

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

pytanie dotyczy łączenia dykt z tym samym kluczem. twoja odpowiedź nie jest wymagana.
Pbd
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.