简体   繁体   中英

Combined list and dict comprehension

I have some kind of verbose logic that I'd like to compact down with some comprehensions.

Essentially, I have a dict object that I'm reading from which has 16 values in it that I'm concerned with. I'm getting the keys that I want with the following comprehension:

["I%d" % (i,) for i in range(16)]

The source dictionary kind of looks like this:

{ "I0": [0,1,5,2], "I1": [1,3,5,2], "I2": [5,9,10,1], ... }

I'd like to essentially map this dictionary to be something like this:

[ 
    { "I0": 0, "I1": 1, "I2": 5, ... }
    { "I0": 1, "I1": 3, "I2": 9, ... }
    ...
]

How can I map things with list and dictionary comprehensions to transform my source dict into my destination list of dictionaries?

This is a fully functional solution that can be applied on arbitrary size.

d = { "I0": [0,1,5,2], "I1": [1,3,5,2], "I2": [5,9,10,1]}
map(dict, zip(*map(lambda (k, v): map(lambda vv: (k, vv), v), d.iteritems())))

to elaborate: (I'm using ipython and the underscore _ means the previous output)

In [1]: d = {'I0': [0, 1, 5, 2], 'I1': [1, 3, 5, 2], 'I2': [5, 9, 10, 1]}

In [2]: map(lambda (k, v): map(lambda vv: (k, vv), v), _.iteritems())
Out[2]: 
[[('I1', 1), ('I1', 3), ('I1', 5), ('I1', 2)],
 [('I0', 0), ('I0', 1), ('I0', 5), ('I0', 2)],
 [('I2', 5), ('I2', 9), ('I2', 10), ('I2', 1)]]

In [3]: zip(*_)
Out[3]: 
[(('I1', 1), ('I0', 0), ('I2', 5)),
 (('I1', 3), ('I0', 1), ('I2', 9)),
 (('I1', 5), ('I0', 5), ('I2', 10)),
 (('I1', 2), ('I0', 2), ('I2', 1))]

In [4]: map(dict, _)
Out[4]: 
[{'I0': 0, 'I1': 1, 'I2': 5},
 {'I0': 1, 'I1': 3, 'I2': 9},
 {'I0': 5, 'I1': 5, 'I2': 10},
 {'I0': 2, 'I1': 2, 'I2': 1}]

How I would solve it:

Firstly, I would get for each key a list containing tuples, where the first item would be the key and the second one would be one of the values from the list:

>>> [ [ (k, i) for i in l] for k, l in d.items() ]
 [[('I1', 1), ('I1', 3), ('I1', 5), ('I1', 2)],
  [('I0', 0), ('I0', 1), ('I0', 5), ('I0', 2)],
  [('I2', 5), ('I2', 9), ('I2', 10), ('I2', 1)]]

Then I would transverse this list, creating a list of tuples containing each corresponding key, using the zip function:

>>> list(zip(*[ [ (k, i) for i in l] for k, l in d.items() ]))
[(('I1', 1), ('I0', 0), ('I2', 5)),
 (('I1', 3), ('I0', 1), ('I2', 9)),
 (('I1', 5), ('I0', 5), ('I2', 10)),
  (('I1', 2), ('I0', 2), ('I2', 1))]

These sublists can passed as parameter to the dict constructor:

>>> [dict(lp) for lp in zip(*[ [ (k, i) for i in l] for k, l in d.items() ])]
[{'I0': 0, 'I1': 1, 'I2': 5},
 {'I0': 1, 'I1': 3, 'I2': 9},
 {'I0': 5, 'I1': 5, 'I2': 10},
 {'I0': 2, 'I1': 2, 'I2': 1}]

In practice, however, I would never recommend one to do such a thing in one line only:

>>> pairs = [ [ (k, i) for i in l] for k, l in d.items() ]
>>> transversed = zip(*pairs)
>>> ds = [dict(t) for t in transversed]
>>> pprint(ds)
[{'I0': 0, 'I1': 1, 'I2': 5},
 {'I0': 1, 'I1': 3, 'I2': 9},
 {'I0': 5, 'I1': 5, 'I2': 10},
 {'I0': 2, 'I1': 2, 'I2': 1}]

Actually, I would say I posted this answer mostly to suggest you to split your solution in more than one line.

There is short and easy solution:

keys = data.keys()
values = data.values()
transformed = [dict(zip(keys, t)) for t in zip(*values)]

The key here is transposing values matrix, which is done with zip(*values) , then we just reconstruct dicts.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM