Reputation: 2162
I have a dictionary with a mix of strings and lists:
sample_dict = {'numbers': [1, 2, 3], 'ref': 'some text'}
Goal is to combine and "flatten" it into an iterable of individual dictionaries that will then be used as parameters for a set of scheduled tasks via Celery:
# end goal:
[{'numbers': 1, 'ref': 'some text'},
{'numbers': 2, 'ref': 'some text'},
{'numbers': 3, 'ref': 'some text'}]
I tried to write it using a traditional loop to keep the code simple for later adaptation or bugfixing but I couldn't work out a way that would have access to all the necessary nested variables.
Result was to use a couple of relatively complex list comprehensions:
raw_vals = [[(i,v)] if isinstance(v,str) else ([(i,b) for b in v]) for i,v in sample_dict.items()]
end_goal = [dict(i) for i in product(*raw_vals)]
Question therefore: is there a more verbose but potentially less cryptic way to get the same result, using a standard loop or similar? I know this potentially gets into a debate about readability vs LOC, but try to ignore that if you can.
Upvotes: 1
Views: 43
Reputation: 77902
Either there's something missing from your question or you overlooked the obvious:
>>> sample_dict = {'numbers': [1, 2, 3], 'ref': 'some text'}
>>> flattened = [{'numbers': num, 'ref': sample_dict['ref']} for num in sample_dict['numbers']]
>>> print flattened
[{'ref': 'some text', 'numbers': 1}, {'ref': 'some text', 'numbers': 2}, {'ref': 'some text', 'numbers': 3}]
Upvotes: 0
Reputation: 74645
I'd suggest factoring out the conditional out of the first:
def assoc(i, v):
if isinstance(v, list):
return [(i, b) for b in v]
return [(i, v)]
raw_vals = [assoc(i, v) for i, v in sample_dict.items())]
end_goal = [dict(i) for i in product(*raw_vals)]
Then the comprehensions are maps.
I also might suggest testing if the value is a list rather than a string as this would permit values to be any type other than a list and not be iterated over. This would also permit subtypes of lists to be treated as lists.
Upvotes: 2