2011-03-31 3 views
19

Étant donné n listes avec m dictionnaires comme éléments, je voudrais produire une nouvelle liste, avec un ensemble de dictionnaires joints. Chaque dictionnaire est garanti d'avoir une clé appelée "index", mais pourrait avoir un ensemble arbitraire de clés au-delà de cela. Les clés sans index ne se chevaucheront jamais entre les listes. Par exemple, imaginez les deux listes suivantes:joindre deux listes de dictionnaires sur une seule clé

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

("b" aurait jamais apparaître dans l2, car il est apparu dans l1 et, de même, "c" aurait jamais apparaître dans l1, car il est apparu dans l2)

Je voudrais produire une liste jointe:

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

Quelle est la manière la plus efficace de faire ceci en Python?

+0

Est-il garanti que la valeur de l'entrée "index" dans la dict correspondra à la position de cette dict dans la liste? –

+0

Non - Il n'est pas garanti que "index" correspondrait à la position de la dict dans la liste. – Bacon

Répondre

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

EDIT: Depuis l3 n'est pas garanti à trier (.values() articles de retours sans ordre spécifique), vous pouvez faire comme @ user560833 suggère:

from operator import itemgetter 

... 

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

Était sur le point de mettre en œuvre quelque chose de très proche de cela. –

+2

Vous devrez trier ensuite l3 - il n'est pas garanti que la liste sera dans l'ordre de l'index. par exemple. 'à partir de l'opérateur importateur d'articles; l3.sort (key = itemgetter ("index")) ' –

+1

A la place de' pour l dans (l1, l2): pour elem dans l: 'il vaut mieux utiliser directement' pour elem dans itertools.chain (l1, l2) ' – klis87

1

Voici un one-liner qui fait ceci:

[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])]] 

Pas tout à fait aussi élégant qu'une compréhension de liste. Je ne pense pas que le résultat soit nécessairement trié comme vous le souhaitez.

Extension de la seule ligne:

[ 
    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] 
     ) 
    ] 
] 

L'expression de consigne sur la sixième ligne reçoit toutes les valeurs d'index uniques des deux listes. La liste de compréhension autour de cela (lignes 3-9) crée une liste de listes où chaque liste interne est une liste combinée de dictionnaires pour cet index/clé avec une valeur d'index particulier. La compréhension de la liste la plus à l'extérieur crée une liste unique de paires de tuple pour chaque clé et la convertit en une liste de dictionnaires.