Як об’єднати кілька диктів з одним ключем?


88

У мене є кілька пар диктовок / ключ-значення, як це:

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

Я хочу, щоб результатом був новий дикт (найефективніший спосіб, якщо це можливо):

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

Власне, я хочу, щоб результат d був:

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

Якщо хтось покаже мені, як отримати перший результат, я можу зрозуміти решту.


4
@Salil: Чи можна припустити, що кожен ключ присутній у всіх словниках?
Björn Pollex

можливий дублікат злиття словників Python
Johnsyweb

Привіт Space_C0wb0y, так, клавіші присутні у всіх словниках.
Саліл

Дуже важливо визначити, чи всі дикти мають однакові клавіші.
югр

Відповіді:


46

припускаючи, що всі клавіші завжди присутні у всіх диктах:

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

Примітка: У Python 3.x використовуйте код нижче:

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

а якщо дик містить масиви numpy:

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

3
Тільки "для k в d1", я думаю.
Саліл

та d.get (k, None) замість d [k]
тахір

1
@tahir Це означало б, що в диктофонах є невідповідні клавіші, тому ітерація над d1неправильною (вона може пропустити клавіші в інших диктах).
югр

1
Для користувачів python 3: d1.iterkeys () = d1.items ()
Райлі

Це все ще не працює для мене в Python3.x. Я спробував це, навіть якщо мої значення не є масивами, і це працює. Однак на виході значень будуть масиви. stackoverflow.com/questions/54040858/…
Ric S,

74

Ось загальне рішення, яке обробляє довільну кількість словників, у випадках, коли ключі є лише у деяких словниках:

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)

Показує:

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

Крім того, щоб отримати свій .attrib, просто перейдіть append(value)наappend(value.attrib)


Я думаю, що ОП бажає цінностей як tupleні list.
user225312

1
@AA: це насправді має значення? кортежі буде складніше будувати в більш загальному випадку з множинними ввідними диктами, де деякі клавіші присутні не скрізь, імхо
Елі Бендерскі

1
Тоді ви можете захотіти зробити нормальне dictз того, defaultdictщоб у вас була нормальна dictповедінка для неіснуючих ключів тощо: dd = dict(dd)
Нед Дейлі

@Ned: хороший момент, але це залежить від можливого використання даних
Елі Бендерський

@Eli: Ні, це не має значення, але я просто намагався базуватись на тому, що хотів ОП, і сподівався, що від вас буде рішення для кортежів :-)
user225312

4

Якщо у вас є лише d1 і d2,

from collections import defaultdict

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

4

Ось один із підходів, який можна використовувати, який би працював, навіть якщо обидва словники не мають однакових ключів:

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

Це призведе до введення нижче:

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

Можна set(d1.keys() + d2.keys()) змінити на set(list(d1.keys()) + list(d2.keys()))у відповіді (для Python 3.x)? В іншому випадку це призведе до 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}

Переконайтесь, що ключі розташовані в однаковому порядку:

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

дає:

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

2
Порядок елементів у values()невизначений, тому ви можете об’єднувати значення з не пов’язаних між собою ключів.
югр

Я щойно застосував зміни, щоб тепер він міг захопити ваш відгук
Махді Гелічі

Я не думаю, що зміна вирішить проблему. Вам потрібно використовувати sorted(d.items())або sorted(d.keys())для досягнення передбачуваних результатів.
югр

Чи можете ви навести приклад, який підтверджує протилежне? dict2_sorted - це відсортований словник у python!
Махді Гелічі

1
Я зробив невелике дослідження з цього приводу. В останніх версіях Python (3.6+) порядок ітерацій почав відповідати порядку вставки (див., Наприклад, тут ), що змушує ваш код проходити. Але це вважається деталлю реалізації, на яку не слід покладатися. Мій другий приклад (див. Тут ) надійно не працює в onlinegdb, який використовує старий Python 3.4. Інші перекладачі в Інтернеті використовують новіші Pythons, тому випуск там не може бути відтворений.
югр

2

Ця функція об'єднує два дикти, навіть якщо клавіші в двох словниках різні:

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

Приклад:

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

Відповідь Елі Бендерського:

Python 3 видалив dict.iteritems, а не dict.items. Дивіться вікі-версію Python: 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

Припустимо, у вас є список ВСІХ ключів (ви можете отримати цей список, перебираючи всі словники та отримуючи їхні ключі). Давайте назвемо це listKeys. Також:

  • listValues - це список ВСІХ значень для одного ключа, який потрібно об’єднати.
  • allDicts: усі словники, які ви хочете об’єднати.
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

Щоб доповнити рішення з двох списків, ось рішення для обробки єдиного списку.

Зразок списку (пов'язаний з NetworkX; тут відформатовано вручну для читабельності):

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

Зверніть увагу на повторювані значення для тих самих ребер (визначених кортежами). Щоб порівняти ці "значення" з відповідними "ключами":

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

Якщо потрібно, перетворіть цей список на 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'],
  ...}

Список літератури


0

З відповіді blubb:

Ви також можете безпосередньо сформувати кортеж, використовуючи значення з кожного списку

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

Це може бути корисно, якщо у вас було спеціальне замовлення для своїх кортежів

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

Ця бібліотека мені допомогла, у мене був список вкладених ключів з однаковим іменем, але з різними значеннями, кожне інше рішення перевизначало ці вкладені ключі.

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

Якщо ключі вкладені:

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)

врожайність:

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

-4

Компактна можливість

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

питання полягає в об’єднанні диктів з одним ключем. ви не є необхідною відповіддю.
Pbd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.