Python >> Tutoriel Python >  >> Python

Fusionner deux dictionnaires et conserver les valeurs des clés en double en Python

Vous pouvez utiliser un defaultdict pour contenir des listes, puis ajoutez-y simplement les valeurs. Cette approche s'étend facilement à un nombre arbitraire de dictionnaires.

from collections import defaultdict

dd = defaultdict(list)

dics = [dic1, dic2]
for dic in dics:
    for key, val in dic.iteritems():  # .items() in Python 3.
        dd[key].append(val)

>>> dict(dd)
{'first': [1, 9], 'fourth': [3], 'second': [4, 5], 'third': [8]}

Toutes les clés avec une seule valeur sont toujours conservées dans une liste, ce qui est probablement la meilleure solution. Vous pouvez cependant changer n'importe quoi de longueur un en valeur réelle, par exemple

for key, val in dd.iteritems():  # .items() in Python 3.
    if len(val) == 1
        dd[key] = val[0]

Voici une solution naïve; copiez l'un des dictionnaires dans le résultat et parcourez les clés et les valeurs de l'autre dictionnaire, en ajoutant des listes au résultat si nécessaire. Puisqu'il n'y a que deux dictionnaires, aucune liste fusionnée n'aura plus de 2 éléments.

dic1 = {"first": 1, "second": 4, "third": 8} 
dic2 = {"first": 9, "second": 5, "fourth": 3}
dic3 = dict(dic2)

for k, v in dic1.items():
    dic3[k] = [dic3[k], v] if k in dic3 else v

print(dic3) # => {'first': [9, 1], 'second': [5, 4], 'fourth': 3, 'third': 8}

Si vous souhaitez que les valeurs uniques soient des listes (probablement une meilleure conception ; les types mixtes ne sont pas très amusants à gérer), vous pouvez utiliser :

dic3 = {k: [v] for k, v in dic2.items()}

for k, v in dic1.items():
    dic3[k] = dic3[k] + [v] if k in dic3 else [v]

print(dic3) # => {'first': [9, 1], 'second': [5, 4], 'fourth': [3], 'third': [8]}

Généraliser à n'importe quel nombre de dictionnaires :

def merge_dicts(*dicts):
    """
    >>> merge_dicts({"a": 2}, {"b": 4, "a": 3}, {"a": 1})
    {'a': [2, 3, 1], 'b': [4]}
    """
    merged = {}
    
    for d in dicts:
        for k, v in d.items():
            if k not in merged:
                merged[k] = []

            merged[k].append(v)
    
    return merged

Vous pouvez utiliser collections.defaultdict pour le nettoyer un peu si l'import ne vous dérange pas :

from collections import defaultdict

def merge_dicts(*dicts):
    """
    >>> merge_dicts({"a": 2}, {"b": 4, "a": 3}, {"a": 1})
    defaultdict(<class 'list'>, {'a': [2, 3, 1], 'b': [4]})
    """
    merged = defaultdict(list)
    
    for d in dicts:
        for k, v in d.items():
            merged[k].append(v)
    
    return merged

Donné :

dic1 =  { "first":1, "second":4, "third":8} 
dic2 =  { "first":9, "second":5, "fourth":3}

Vous pouvez utiliser .setdefault :

dic_new={}
for k,v in list(dic1.items())+list(dic2.items()):
    dic_new.setdefault(k, []).append(v)
else:
    dic_new={k:v if len(v)>1 else v[0] for k,v in dic_new.items()}  

>>> dic_new
{'first': [1, 9], 'second': [4, 5], 'third': 8, 'fourth': 3}

Cela produit la sortie en question. Je pense que l'aplatissement des listes d'éléments uniques en un type d'objet différent est une complexité inutile.

Avec la modification, cela produit le résultat souhaité :

dic_new={}
for k,v in list(dic1.items())+list(dic2.items()):
    dic_new.setdefault(k, []).append(v)

>>> dic_new
{'first': [1, 9], 'second': [4, 5], 'third': [8], 'fourth': [3]}