Je dois fusionner la liste du dictionnaire python. Par exemple:
dicts[0] = {'a':1, 'b':2, 'c':3}
dicts[1] = {'a':1, 'd':2, 'c':'foo'}
dicts[2] = {'e':57,'c':3}
super_dict = {'a':[1], 'b':[2], 'c':[3,'foo'], 'd':[2], 'e':[57]}
J'ai écrit le code suivant:
super_dict = {}
for d in dicts:
for k, v in d.items():
if super_dict.get(k) is None:
super_dict[k] = []
if v not in super_dict.get(k):
super_dict[k].append(v)
Peut-il être présenté de manière plus élégante/optimisée?
Remarque J'ai trouvé un autre question sur SO mais il s'agit de fusionner exactement 2 dictionnaires.
Vous pouvez parcourir les dictionnaires directement - pas besoin d'utiliser range
. La méthode setdefault
de dict recherche une clé et renvoie la valeur si elle est trouvée. S'il n'est pas trouvé, il renvoie une valeur par défaut et affecte également cette valeur par défaut à la clé.
super_dict = {}
for d in dicts:
for k, v in d.iteritems(): # d.items() in Python 3+
super_dict.setdefault(k, []).append(v)
Vous pouvez également envisager d'utiliser un defaultdict
. Cela automatise simplement setdefault
en appelant une fonction pour renvoyer une valeur par défaut lorsqu'une clé n'est pas trouvée.
import collections
super_dict = collections.defaultdict(list)
for d in dicts:
for k, v in d.iteritems(): # d.items() in Python 3+
super_dict[k].append(v)
En outre, comme Sven Marnach astucieusement observé, vous semblez ne vouloir aucune duplication de valeurs dans vos listes. Dans ce cas, set
vous donne ce que vous voulez:
import collections
super_dict = collections.defaultdict(set)
for d in dicts:
for k, v in d.iteritems(): # d.items() in Python 3+
super_dict[k].add(v)
from collections import defaultdict
dicts = [{'a':1, 'b':2, 'c':3},
{'a':1, 'd':2, 'c':'foo'},
{'e':57, 'c':3} ]
super_dict = defaultdict(set) # uses set to avoid duplicates
for d in dicts:
for k, v in d.items(): # use d.iteritems() in python 2
super_dict[k].add(v)
Fusionnez les clés de tous les dict, et pour chaque clé, assemblez la liste des valeurs:
super_dict = {}
for k in set(k for d in dicts for k in d):
super_dict[k] = [d[k] for d in dicts if k in d]
L'expression set(k for d in dicts for k in d)
construit un ensemble de toutes les clés uniques de tous les dictionnaires. Pour chacune de ces clés uniques, nous utilisons la compréhension de liste [d[k] for d in dicts if k in d]
Pour construire la liste des valeurs de tous les dictés pour cette clé.
Puisque vous ne semblez utiliser qu'une seule valeur nique de chaque clé, vous pouvez utiliser des ensembles à la place:
super_dict = {}
for k in set(k for d in dicts for k in d):
super_dict[k] = set(d[k] for d in dicts if k in d)
N'oubliez jamais que les bibliothèques standard disposent d'une multitude d'outils pour gérer les dict et l'itération:
from itertools import chain
from collections import defaultdict
super_dict = defaultdict(list)
for k,v in chain.from_iterable(d.iteritems() for d in dicts):
if v not in super_dict[k]: super_dict[k].append(v)
Notez que if v not in super_dict[k]
Peut être évité en utilisant defaultdict(set)
selon la réponse de Steven Rumbalski.
Lorsque la valeur des clés est dans la liste:
from collections import defaultdict
dicts = [{'a':[1], 'b':[2], 'c':[3]},
{'a':[11], 'd':[2], 'c':['foo']},
{'e':[57], 'c':[3], "a": [1]} ]
super_dict = defaultdict(list) # uses set to avoid duplicates
for d in dicts:
for k, v in d.items(): # use d.iteritems() in python 2
super_dict[k] = list(set(super_dict[k] + v))
combined_dict = {}
for elem in super_dict.keys():
combined_dict[elem] = super_dict[elem]
combined_dict
## output: {'a': [1, 11], 'b': [2], 'c': [3, 'foo'], 'd': [2], 'e': [57]}
Cela peut être un peu plus élégant:
super_dict = {}
for d in dicts:
for k, v in d.iteritems():
l=super_dict.setdefault(k,[])
if v not in l:
l.append(v)
MISE À JOUR: modification apportée par Sven
MISE À JOUR: changé pour éviter les doublons (merci Marcin et Steven)
Pour un oneliner, les éléments suivants pourraient être utilisés:
{key: {d[key] for d in dicts if key in d} for key in {key for d in dicts for key in d}}
bien que la lisibilité gagnerait à nommer l'ensemble de clés combiné:
combined_key_set = {key for d in dicts for key in d}
super_dict = {key: {d[key] for d in dicts if key in d} for key in combined_key_set}
L'élégance peut être débattue mais personnellement je préfère les compréhensions aux boucles. :)
(Le dictionnaire et les compréhensions d'ensemble sont disponibles en Python 2.7/3.1 et plus récent.)
Je crains que personne ne l'ait encore posté ..
d = {**one, **two, **three, **four}
print d
ce serait suffisant ..
Ma solution est similaire à @senderle proposée, mais au lieu de pour la boucle, j'ai utilisé la carte
super_dict = defaultdict(set)
map(lambda y: map(lambda x: super_dict[x].add(y[x]), y), dicts)
Si vous supposez que les clés qui vous intéressent sont au même niveau imbriqué, vous pouvez parcourir récursivement chaque dictionnaire et créer un nouveau dictionnaire à l'aide de cette clé, en les fusionnant efficacement.
merged = {}
for d in dicts:
def walk(d,merge):
for key, item in d.items():
if isinstance(item, dict):
merge.setdefault(key, {})
walk(item, merge[key])
else:
merge.setdefault(key, [])
merge[key].append(item)
walk(d,merged)
Par exemple, supposons que vous souhaitiez fusionner les dictionnaires suivants.
dicts = [{'A': {'A1': {'FOO': [1,2,3]}}},
{'A': {'A1': {'A2': {'BOO': [4,5,6]}}}},
{'A': {'A1': {'FOO': [7,8]}}},
{'B': {'B1': {'COO': [9]}}},
{'B': {'B2': {'DOO': [10,11,12]}}},
{'C': {'C1': {'C2': {'POO':[13,14,15]}}}},
{'C': {'C1': {'ROO': [16,17]}}}]
En utilisant la clé à chaque niveau, vous devriez obtenir quelque chose comme ceci:
{'A': {'A1': {'FOO': [[1, 2, 3], [7, 8]],
'A2': {'BOO': [[4, 5, 6]]}}},
'B': {'B1': {'COO': [[9]]},
'B2': {'DOO': [[10, 11, 12]]}},
'C': {'C1': {'C2': {'POO': [[13, 14, 15]]},
'ROO': [[16, 17]]}}}
Remarque: Je suppose que la feuille à chaque branche est une liste quelconque, mais vous pouvez évidemment changer la logique pour faire tout ce qui est nécessaire pour votre situation.
L'utilisation de defaultdict est bonne, cela peut également être fait avec l'utilisation de itertools.groupby.
import itertools
# output all dict items, and sort them by key
dicts_ele = sorted( ( item for d in dicts for item in d.items() ), key = lambda x: x[0] )
# groups items by key
ele_groups = itertools.groupby( dicts_ele, key = lambda x: x[0] )
# iterates over groups and get item value
merged = { k: set( v[1] for v in grouped ) for k, grouped in ele_groups }
et évidemment, vous pouvez fusionner ce bloc de code dans un style à une ligne
merged = {
k: set( v[1] for v in grouped )
for k, grouped in (
itertools.groupby(
sorted(
( item for d in dicts for item in d.items() ),
key = lambda x: x[0]
),
key = lambda x: x[0]
)
)
}