Question

Imagine I have these python lists:

keys = ['name', 'age']
values = ['Monty', 42, 'Matt', 28, 'Frank', 33]

Is there a direct or at least a simple way to produce the following list of dictionaries ?

[
    {'name': 'Monty', 'age': 42},
    {'name': 'Matt',  'age': 28},
    {'name': 'Frank', 'age': 33}
]
Was it helpful?

Solution

Here is the zip way

def mapper(keys, values):
    n = len(keys)
    return [dict(zip(keys, values[i:i + n]))
            for i in range(0, len(values), n)]

OTHER TIPS

It's not pretty but here's a one-liner using a list comprehension, zip and stepping:

[dict(zip(keys, a)) for a in zip(values[::2], values[1::2])]

Dumb way, but one that comes immediately to my mind:

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 nearly does what you want; unfortunately, rather than cycling the shorter list, it breaks. Perhaps there's a related function that cycles?

$ python
>>> keys = ['name', 'age']
>>> values = ['Monty', 42, 'Matt', 28, 'Frank', 33]
>>> dict(zip(keys, values))
{'age': 42, 'name': 'Monty'}

/EDIT: Oh, you want a list of dict. The following works (thanks to Peter, as well):

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

In the answer by Konrad Rudolph

zip nearly does what you want; unfortunately, rather than cycling the shorter list, it breaks. Perhaps there's a related function that cycles?

Here's a way:

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

I will not call it Pythonic (I think it's too clever), but it might be what are looking for.

There is no benefit in cycling the keys list using itertools.cycle(), because each traversal of keys corresponds to the creation of one dictionnary.

EDIT: Here's another way:

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

This is much more pythonic: there's a readable utility function with a clear purpose, and the rest of the code flows naturally from it.

Here's my simple approach. It seems to be close to the idea that @Cheery had except that I destroy the input list.

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

Yet another try, perhaps dumber than the first one:

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

But it's up to you to decide whether it's dumber.

[dict(zip(keys,values[n:n+len(keys)])) for n in xrange(0,len(values),len(keys)) ]

UG-LEEE. I'd hate to see code that looks like that. But it looks right.

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 )

Still a little ugly, but the names help make sense of it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top