2012-02-23 14 views
22

Tengo que fusionar la lista del diccionario python. Por ejemplo:fusionando varios diccionarios python

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

me escribió el siguiente código:

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) 

¿Puede ser presentados de manera más elegante/optimizado?

Nota he encontrado otra question el SO pero su acerca de cómo combinar exactamente 2 diccionarios.

+0

@SvenMarnach Eso es demasiado generoso de su parte! Hecho. + 1ed tu respuesta también :) – jerrymouse

+0

** Ver también **: [fusionar diccionario anidado] (https://stackoverflow.com/questions/7204805/dictionaries-of-dictionaries-merge) – dreftymac

+0

** Ver también: ** [ pregunta similar] (https://stackoverflow.com/questions/2365921/merging-python-dictionaries) – dreftymac

Respuesta

24

Puede iterar sobre los diccionarios directamente - sin necesidad de utilizar range. El método setdefault de dict busca una clave y devuelve el valor si se encuentra. Si no se encuentra, devuelve un valor predeterminado, y también asigna ese valor predeterminado a la clave.

super_dict = {} 
for d in dicts: 
    for k, v in d.iteritems(): # d.items() in Python 3+ 
     super_dict.setdefault(k, []).append(v) 

Además, es posible considerar el uso de un defaultdict. Esto simplemente automatiza setdefault llamando a una función para devolver un valor predeterminado cuando no se encuentra una clave.

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) 

También, como Sven Marnach observó astutamente, que parecen querer hay duplicación de los valores en sus listas. En ese caso, set le consigue lo que quiere:

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

Esto puede ser un poco más elegante:

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) 

ACTUALIZACIÓN: cambio realizado sugerido por Sven

ACTUALIZACIÓN: se ha cambiado para evitar duplicados (gracias Marcin y Steven)

+0

Agradable. Sugeriría 'for d in dicts:' en lugar de 'for i in xrange (len (dicts))'. –

+1

@SvenMarnach Bien, hice ese cambio. –

+1

Min nit. Duplica en la clave ''c'':' [3,' foo ', 3] '. El código de ejemplo de OP muestra que el '3' no se repite. –

11

Combinar las claves de todos los dictados, y para cada clave, arme la lista de valores:

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] 

e xpression set(k for d in dicts for k in d) crea un conjunto de todas las claves exclusivas de todos los diccionarios. Para cada una de estas claves únicas, usamos la lista de comprensión [d[k] for d in dicts if k in d] para compilar la lista de valores de todos los dictados de esta clave.

Ya que parece sólo a un único valor de la de cada tecla, es posible que desee utilizar una configuración en su lugar:

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

Bastante sólido. Creo que podría mejorarse con una explicación. – Edwin

+0

@Edwin: Gracias, agregué un poco de explicación. –

+0

@SvenMarnach cosa menor - con la segunda versión, obtenemos un dict de sets en lugar de un dict of lists - se maneja fácilmente si le importa a OP. –

3

Nunca hay que olvidar que las bibliotecas estándar tienen una gran cantidad de herramientas para hacer frente a dicts y de iteración:

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) 

Tenga en cuenta que la if v not in super_dict[k] se pueden evitar usando defaultdict(set) según la respuesta de Steven Rumbalski.

17
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.iteritems(): 
     super_dict[k].add(v) 
+1

+1: Esto parece ser exactamente lo que la pregunta estaba pidiendo (elementos únicos en los valores), hecho de una manera relativamente clara y sin duda eficiente (los diccionarios se han ido una sola vez, y el conjunto incorporado hace que se mantengan solo elementos únicos rápidos). – EOL

-2

estoy un poco tarde para el juego, pero lo hice en 2 líneas, sin dependencias más allá de pitón en sí:

flatten = lambda *c: (b for a in c for b in (flatten(*a) if isinstance(a, (tuple, list)) else (a,))) 
o = reduce(lambda d1,d2: dict((k, list(flatten([d1.get(k), d2.get(k)]))) for k in set(d1.keys() + d2.keys())), dicts) 
# output: 
# {'a': [1, 1, None], 'c': [3, 'foo', 3], 'b': [2, None, None], 'e': [None, 57], 'd': [None, 2, None]} 

Aunque si no se preocupan por las listas anidadas, a continuación:

o2 = reduce(lambda d1,d2: dict((k, [d1.get(k), d2.get(k)]) for k in set(d1.keys() + d2.keys())), dicts) 
# output: 
# {'a': [[1, 1], None], 'c': [[3, 'foo'], 3], 'b': [[2, None], None], 'e': [None, 57], 'd': [[None, 2], None]} 
+1

No se deben crear valores 'Ninguno', según la pregunta. – EOL

1

Para una oneliner, la siguiente podría ser utilizado:

{key: {d[key] for d in dicts if key in d} for key in {key for d in dicts for key in d}}

aunque legibilidad se beneficiaría de nombrar el conjunto de claves combinada:

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} 

elegancia puede ser debatido, pero personalmente prefiero comprensiones sobre los bucles. :)

(El diccionario y comprensiones establecidos están disponibles en Python 2.7/3.1 y más recientes.)

-1

Parece que la mayoría de las respuestas usando comprensiones no son tan fácil de leer. En caso de que se pierda en el desorden de las respuestas anteriores, esto podría ser útil (aunque muy tarde ...). Simplemente recorra los ítems de cada dict y ubíquelos en uno separado.

super_dict = {key:val for d in dicts for key,val in d.items()} 
+1

OP desea conservar ambos valores ''c'':' {' c ': [3,' foo ']} '. –

0

Mi solución es similar a @senderle propuesto, pero en lugar de bucle Solía ​​mapa

super_dict = defaultdict(set) 
map(lambda y: map(lambda x: super_dict[x].add(y[x]), y), dicts) 
Cuestiones relacionadas