2011-03-31 29 views
19

Dada n listas con m diccionarios como sus elementos, me gustaría producir una nueva lista, con un conjunto unido de diccionarios. Cada diccionario tiene garantizada una clave llamada "índice", pero podría tener un conjunto arbitrario de claves más allá de eso. Las claves que no son índice nunca se superpondrán en todas las listas. Por ejemplo, imagina las dos listas siguientes:unir dos listas de diccionarios en una sola clave

l1 = [{"index":1, "b":2}, {"index":2, "b":3}, {"index":3, "green":"eggs"}] 
l2 = [{"index":1, "c":4}, {"index":2, "c":5}] 

("b" nunca aparecería en l2, ya que apareció en l1, y de manera similar, "c" nunca aparecería en l1, ya que apareció en l2)

Me gustaría producir una lista unida:

l3 = [{"index":1, "b":2, "c":4}, 
     {"index":2, "b":3, "c":5}, 
     {"index":3, "green":"eggs"}] 

¿Cuál es la forma más eficiente de hacer esto en Python?

+0

¿Se garantiza que el valor de la entrada "index" en el dict coincidirá con la posición de ese dict en la lista? –

+0

No, no se garantiza que el "índice" coincida con la posición del dict en la lista. – Bacon

Respuesta

31
from collections import defaultdict 

l1 = [{"index":1, "b":2}, {"index":2, "b":3}, {"index":3, "green":"eggs"}] 
l2 = [{"index":1, "c":4}, {"index":2, "c":5}] 

d = defaultdict(dict) 
for l in (l1, l2): 
    for elem in l: 
     d[elem['index']].update(elem) 
l3 = d.values() 

# l3 is now: 

[{'b': 2, 'c': 4, 'index': 1}, 
{'b': 3, 'c': 5, 'index': 2}, 
{'green': 'eggs', 'index': 3}] 

EDITAR: Desde l3 no está garantizada a ser clasificadas (.values() posiciones de devolución en ningún orden específico), que puede hacer como @ user560833 sugiere:

from operator import itemgetter 

... 

l3 = sorted(d.values(), key=itemgetter("index")) 
+0

Estaba a punto de implementar algo muy cercano a esto. –

+2

Deberá ordenar posteriormente l3; no se garantiza que la lista esté en orden del índice. p.ej. 'desde el elemento de importación importador del operador; l3.sort (key = itemgetter ("index")) ' –

+1

En vez de' para l in (l1, l2): para elem en l: 'es mejor usar directamente' para elem en itertools.chain (l1, l2) ' – klis87

1

Aquí hay una sola línea que hace esto:

[dict(sum([z.items() for z in z2],[])) for z2 in [[x3 for x3 in l1+l2 if x3['index']==key] for key in set([x1['index'] for x1 in l1]+[x2['index'] for x2 in l2])]] 

No es tan elegante como una lista de comprensión. No creo que el resultado esté garantizado necesariamente para ser ordenado de la manera que desee tampoco.

La ampliación de la de una sola línea:

[ 
    dict(sum([z.items() for z in z2],[])) 
    for z2 in [ 
     [ 
      x3 for x3 in l1+l2 if x3['index']==key 
     ] for key in set(
      [x1['index'] for x1 in l1]+[x2['index'] for x2 in l2] 
     ) 
    ] 
] 

La expresión conjunto en la sexta línea obtiene todos los valores de índice únicos de ambas listas. La comprensión de la lista (líneas 3-9) crea una lista de listas donde cada lista interna es una lista combinada de diccionarios para ese índice/clave con un valor de índice particular. La lista de comprensión más externa crea una lista única de tuplas para cada clave y la convierte de nuevo en una lista de diccionarios.

Cuestiones relacionadas