Mappez deux listes en une seule liste de dictionnaires
-
04-07-2019 - |
Question
Imaginez que j'ai ces listes python:
keys = ['name', 'age']
values = ['Monty', 42, 'Matt', 28, 'Frank', 33]
Existe-t-il un moyen simple ou au moins simple de produire la liste de dictionnaires suivante?
[
{'name': 'Monty', 'age': 42},
{'name': 'Matt', 'age': 28},
{'name': 'Frank', 'age': 33}
]
La solution
Voici la méthode zip
def mapper(keys, values):
n = len(keys)
return [dict(zip(keys, values[i:i + n]))
for i in range(0, len(values), n)]
Autres conseils
Ce n'est pas joli, mais voici un one-line utilisant une liste comprenant, zip et un pas à pas:
[dict(zip(keys, a)) for a in zip(values[::2], values[1::2])]
Manière stupide, mais qui me vient immédiatement à l'esprit:
def fields_from_list(keys, values):
iterator = iter(values)
while True:
yield dict((key, iterator.next()) for key in keys)
list(fields_from_list(keys, values)) # to produce a list.
zip
fait presque ce que vous voulez; malheureusement, plutôt que de parcourir la liste plus courte, il casse. Peut-être y at-il une fonction connexe qui cycle?
$ python
>>> keys = ['name', 'age']
>>> values = ['Monty', 42, 'Matt', 28, 'Frank', 33]
>>> dict(zip(keys, values))
{'age': 42, 'name': 'Monty'}
/ EDIT: Oh, vous voulez une liste de dict . Les œuvres suivantes (merci également à Peter):
from itertoos import cycle
keys = ['name', 'age']
values = ['Monty', 42, 'Matt', 28, 'Frank', 33]
x = zip(cycle(keys), values)
map(lambda a: dict(a), zip(x[::2], x[1::2]))
Dans la réponse, Konrad Rudolph
zip fait presque ce que vous voulez; malheureusement, plutôt que de parcourir la liste plus courte, il casse. Peut-être y at-il une fonction connexe qui cycle?
Voici un moyen:
keys = ['name', 'age']
values = ['Monty', 42, 'Matt', 28, 'Frank', 33]
iter_values = iter(values)
[dict(zip(keys, iter_values)) for _ in range(len(values) // len(keys))]
Je ne l'appellerai pas Pythonic (je pense que c'est trop intelligent), mais c'est peut-être ce que vous recherchez.
Il n'y a aucun avantage à parcourir la liste keys
à l'aide de itertools
.cycle ()
, car chaque parcours de clés
correspond à la création d'un dictionnaire.
MODIFIER: une autre façon de procéder:
def iter_cut(seq, size):
for i in range(len(seq) / size):
yield seq[i*size:(i+1)*size]
keys = ['name', 'age']
values = ['Monty', 42, 'Matt', 28, 'Frank', 33]
[dict(zip(keys, some_values)) for some_values in iter_cut(values, len(keys))]
C’est beaucoup plus pythonique: il existe une fonction utilitaire lisible avec un objectif clair, et le reste du code en découle naturellement.
Voici mon approche simple. Cela semble être proche de l’idée que @Cheery avait sauf que je détruis la liste des entrées.
def pack(keys, values):
"""This function destructively creates a list of dictionaries from the input lists."""
retval = []
while values:
d = {}
for x in keys:
d[x] = values.pop(0)
retval.append(d)
return retval
Encore un essai, peut-être plus bête que le premier:
def split_seq(seq, count):
i = iter(seq)
while True:
yield [i.next() for _ in xrange(count)]
>>> [dict(zip(keys, rec)) for rec in split_seq(values, len(keys))]
[{'age': 42, 'name': 'Monty'},
{'age': 28, 'name': 'Matt'},
{'age': 33, 'name': 'Frank'}]
Mais c'est à vous de décider s'il est plus bête.
[dict(zip(keys,values[n:n+len(keys)])) for n in xrange(0,len(values),len(keys)) ]
UG-LEEE. Je détesterais voir un code qui ressemble à ça. Mais cela semble juste.
def dictizer(keys, values):
steps = xrange(0,len(values),len(keys))
bites = ( values[n:n+len(keys)] for n in steps)
return ( dict(zip(keys,bite)) for bite in bites )
Toujours un peu moche, mais les noms aident à en comprendre le sens.