Reputation: 477
Having following dict structure
>>> d = {
'email': ['e_val1', 'e_val2', 'e_val3', 'e_val4', 'e_val5'],
'id' : ['i_val1', 'i_val2', 'i_val3', 'i_val4'],
'ref' : ['r_val1', 'r_val2', 'r_val3', 'r_val4']
}
what would be an effective way to get the following list of individual dicts?
>>> l = [
{'email': 'e_val1', 'id': 'i_val1', 'ref': 'r_val1'},
{'email': 'e_val2', 'id': 'i_val2', 'ref': 'r_val2'},
{'email': 'e_val3', 'id': 'i_val3', 'ref': 'r_val3'},
{'email': 'e_val4', 'id': 'i_val4', 'ref': 'r_val4'},
{'email': 'e_val5', 'id': None, 'ref': None}
]
So far, i tried:
def split(d):
l, longest = [], False
for k, v in d.items():
longest = max(longest, len(v))
for pointer in range(longest):
r = {}
for k, v in d.items():
try:
r[k] = v[pointer]
except IndexError:
# current list is shorter than longest
r[k] = None
l.append(r)
return l
which shortly after became
from itertools import izip_longest
def split(d):
"""
With Python < 2.7,
- itertools.izip_longest(*d.values())
might be substituted by map with None:
- map(None, *d.values())
"""
_zipper = lambda keys: lambda v: dict(zip(keys, v))
lmb = _zipper(d.keys())
return map(lmb,
itertools.izip_longest(*d.values()))
Assuming Python 2.7.x, in terms of performance, what would be a better way?
>>> from timeit import timeit
>>> # with map
>>> timeit(setup="""
... d={'email': ['e_val1', 'e_val2', 'e_val3', 'e_val4', 'e_val5'],
... 'id': ['i_val1', 'i_val2', 'i_val3', 'i_val4'],
... 'ref': ['r_val1', 'r_val2', 'r_val3', 'i_val4']};
... _zipper=lambda keys: lambda v: dict(zip(keys, v))""",
... stmt="""
... lmb=_zipper(d.keys());
... map(lmb, map(None, *d.values()))""")
16.14903998374939
>>> # with itertools.izip_longest
>>> timeit(setup="""
... d={'email': ['e_val1', 'e_val2', 'e_val3', 'e_val4', 'e_val5'],
... 'id': ['i_val1', 'i_val2', 'i_val3', 'i_val4'],
... 'ref': ['r_val1', 'r_val2', 'r_val3', 'i_val4']};
... _zipper=lambda keys: lambda v: dict(zip(keys, v))""",
... stmt="""
... lmb=_zipper(d.keys());
... map(lmb, izip_longest(*d.values()))""")
18.98265790939331
P.S. For those curious, initial dict is a Django MultiValue QueryDict, containing many <input>
values with same names.
Upvotes: 2
Views: 136
Reputation: 27486
No hardcoded values: timeit : 5.15033793449
def form_dict(key_index):
each_dict = {}
for k in d.keys():
if key_index < len(d[k]):
each_dict[k] = d[k][key_index]
else:
each_dict[k] = None
return each_dict
def get_converted_list():
Max = max([len(v) for v in d.values()])
return map(form_dict, range(0, Max))
Upvotes: 0
Reputation: 42097
Using itertools.zip_longest
and list comprehension:
[{'email': i, 'id': j, 'ref': k} for (i, j, k) in itertools.zip_longest(d.get('email'), d.get('id'), d.get('ref'))]
Example:
>>> d
{'ref': ['r_val1', 'r_val2', 'r_val3', 'r_val4'], 'id': ['i_val1', 'i_val2', 'i_val3', 'i_val4'], 'email': ['e_val1', 'e_val2', 'e_val3', 'e_val4', 'e_val5']}
>>> [{'email': i, 'id': j, 'ref': k} for (i, j, k) in itertools.zip_longest(d.get('email'), d.get('id'), d.get('ref'))]
[{'ref': 'r_val1', 'id': 'i_val1', 'email': 'e_val1'}, {'ref': 'r_val2', 'id': 'i_val2', 'email': 'e_val2'}, {'ref': 'r_val3', 'id': 'i_val3', 'email': 'e_val3'}, {'ref': 'r_val4', 'id': 'i_val4', 'email': 'e_val4'}, {'ref': None, 'id': None, 'email': 'e_val5'}]
Upvotes: 4