Объедините два или более словарей с одинаковыми клавишами - PullRequest
5 голосов
/ 08 мая 2019

У меня есть два словаря items и u_items

items = {"A": 1, "B": 2, "C": 3}

u_items = {"D": 4, "B": 4, "E": 8, "C": 4}

Я хочу обновить словарь элементов с помощью u_items, поэтому я сделал это

items.update((k + '_1' if k in items else k, v) for k, v in u_items.items())

так, что я могу отличить ключи от обоих словарей

Вывод:

items = {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4}

но когда я обновляю словарь элементов другим словарем, скажем, n_items, он заменяет значение B_1вместо того, чтобы делать это B_1_1

n_items = {"C":7, "B":9}

items.update((k + '_1' if k in items else k, v) for k, v in n_items.items())

вывод:

{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 9, 'E': 8, 'C_1': 7}

Но я хочу, чтобы вывод был таким:

{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'B_1_1':9,'C_1_1':7}

или вот так:

{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'B_2':9,'C_2':7}

Как я могу это сделать?

Ответы [ 6 ]

2 голосов
/ 08 мая 2019

Вы можете сделать это итеративно:

def combine(*args):
    result = {}
    for d in args:
        for key, value in d.items():
            key = str(key)
            while key in result:
                key += '_1'
            result[key] = value

    return result

print(combine(items, u_items, n_items))

Выход:

{'A': 1,
 'B': 2,
 'C': 3,
 'D': 4,
 'B_1': 4,
 'E': 8,
 'C_1': 4,
 'C_1_1': 7,
 'B_1_1': 9}
1 голос
/ 08 мая 2019

Хотя это немного похоже на XY Проблема , здесь уродливо (и я уверен, что это неэффективно), а также не очень общее решение, состоящее из:

, который объединяет словари, добавляя " _1 " к существующему ключу ( делает все в одной строке ), как вы и просили, хотя я ' Рекомендую (поскольку есть случаи, когда самое короткое не обязательно лучшее ):

  • Использование функции (чтобы избежать дублирования кода (выражения))
  • Подклассы dict и переопределение его update метода (более хороший вариант предыдущего)
>>> items = {"A": 1, "B": 2, "C": 3}
>>> u_items = {"D": 4, "B": 4, "E": 8, "C": 4}
>>> n_items = {"C": 7, "B": 9}
>>>
>>> items.update((max([k1 + "_1" for k1 in items if k1 == k or k1.startswith(k + "_1")], key=len, default=k), v) for k, v in u_items.items())
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4}
>>>
>>> items.update((max([k1 + "_1" for k1 in items if k1 == k or k1.startswith(k + "_1")], key=len, default=k), v) for k, v in n_items.items())
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'C_1_1': 7, 'B_1_1': 9}
>>>
>>>
>>> # Merging an additional dictionary
...
>>> v_items = {"C": 25}
>>>
>>> items.update((max([k1 + "_1" for k1 in items if k1 == k or k1.startswith(k + "_1")], key=len, default=k), v) for k, v in v_items.items())
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'C_1_1': 7, 'B_1_1': 9, 'C_1_1_1': 25}
0 голосов
/ 08 мая 2019

попробуйте,

без функции, без какого-либо модуля,

>>> for k, v in n_items.items():
...   _k = {k: v}
...   if k in items:
...      _k = {max(i for i in items if k in i)+'_1': v}
...   items.update(_k)
... 
>>> items
{'A': 1, 'D': 4, 'B_1': 4, 'B': 2, 'D_1': 5, 'X': 1, 'C_1_1': 7, 'C': 3, 'E': 8, 'C_1': 4, 'B_1_1': 9}
>>> 
0 голосов
/ 08 мая 2019

Подход автора к обновлению и понимание списка if / else - это действительно хорошее начало. Я думаю, что главное (пожалуйста, извините за каламбур) - ввести цикл, чтобы найти приемлемый ключ. Это python3, но можно воскресить при этом, чтобы сделать его однострочным:

>>> import functools
>>> items = {"A": 1, "B": 2, "C": 3}
>>> u_items = {"D": 4, "B": 4, "E": 8, "C": 4}
>>> n_items = {"C":7, "B":9}
>>> items.update({functools.reduce(lambda c, n: c+n if c in items else c, ['_1']*2, k):*3 v for k, v in u_items.items()})
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4}
>>> items.update({functools.reduce(lambda c,n: c+n if c in items else c, ['_1']*2, k): v for k, v in n_items.items()})
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'C_1_1': 7, 'B_1_1': 9}

Примечание. Ваш список последовательных расширений ['_1']*2 должен быть равен числу списков, которые вы комбинируете. Это упражнение для создания более интересной лямбды, если требуются такие ключи, как C_1, C_2, ....

0 голосов
/ 08 мая 2019

Вы можете использовать небольшую вспомогательную функцию:

d1 = {'A':1, 'B':2, 'B_1':3, 'B_1_1':4}
d2 = {'A':1, 'B':2}

def gen_key(key, dct):
    while key in dct:
        key += '_1'
    return key

d1.update((gen_key(k, d1), v) for k, v in d2.items())

print(d1)
# {'A': 1, 'B': 2, 'B_1': 3, 'B_1_1': 4, 'A_1': 1, 'B_1_1_1': 2}
0 голосов
/ 08 мая 2019

Использование operator.itemgetter:

items = {'A':1, 'B':2, 'C':3}
u_items = {'D':4, 'B':4, 'E':8, 'C':4}
n_items = {"C":7, "B":9}

def update_dict(d1, d2):
    l = list(map(itemgetter(0), d1))
    d1.update(('_'.join([k,str(l.count(k))]) if k in l else k, v) 
             for k,v in d2.items())

update_dict(items, u_items)
update_dict(items, n_items)

Вывод при первом обновлении с использованием u_items:

{'A': 1, 'B': 2, 'B_1': 4, 'C': 3, 'C_1': 4, 'D': 4, 'E': 8}

Выводится при втором обновлении с использованием n_items:

{'A': 1,
 'B': 2,
 'B_1': 4,
 'B_2': 9,
 'C': 3,
 'C_1': 4,
 'C_2': 7,
 'D': 4,
 'E': 8}
...