How can I write a python method to compute all combinations of a dict of lists?
Question:
I have an arbitrary python dictionary x
such that the values of each key is itself a list. Here is an example:
x = {"first_name": ["Mahmoud", "Pei-chi"], "second_name": ["Abadi", "Yao"]}
Given x
I would like write a method that computes a list of dictionaries such that each dictionary has the same keys as x but the values are each combination of individual list element. So in this case, the result should be:
[{"first_name": "Mahmoud", "second_name": "Abadi"},
{"first_name": "Mahmoud", "second_name": "Yao"},
{"first_name": "Pei-chi", "second_name": "Abadi"},
{"first_name": "Pei-chi", "second_name": "Yao"}]
How can I do it? The dictionary x
may have any arbitrary number of keys with arbitrary names.
Answers:
You can issue the following list comprehension.
result = [dict(zip(x.keys(), t)) for t in product(*x.values())]
Python’s itertools.product()
is good for this:
from itertools import product
x = {"first_name": ["Mahmoud", "Pei-chi"], "second_name": ["Abadi", "Yao"]}
dict_list = [{'first_name': f, 'second_name':s} for f, s in product(x['first_name'], x['second_name'])]
Giving you:
[{'first_name': 'Mahmoud', 'second_name': 'Abadi'}, {'first_name': 'Mahmoud', 'second_name': 'Yao'}, {'first_name': 'Pei-chi', 'second_name': 'Abadi'}, {'first_name': 'Pei-chi', 'second_name': 'Yao'}]
If you start from basic operations on list and dictionary with this simple for loops you can do above operations, but yeah it’s not as much optimized as of others in terms of neatness, nut complexity of operations is same.
lis = []
for f in x:
if f =='first_name':
for l in x[f]:
for j in x['second_name']:
dic ={}
dic['first-name']=l;dic['last_name']=j
lis.append(dic)
O/P will be as we expect:
[{'first-name': 'Mahmoud', 'last_name': 'Abadi'},
{'first-name': 'Mahmoud', 'last_name': 'Yao'},
{'first-name': 'Pei-chi', 'last_name': 'Abadi'},
{'first-name': 'Pei-chi', 'last_name': 'Yao'}]
If your dict are unordered don’t worry you can use ordered dict , it will not change the result, it will give the same result every time.
dict_1 = {"first_name": ["Mahmoud", "Pei-chi"], "second_name": ["Abadi", "Yao"]}
import itertools
import collections
from operator import itemgetter
ordered_dict=collections.OrderedDict(sorted(dict_1.items(), key=itemgetter(0)))
items=[]
for key,value in ordered_dict.items():
items.append(value)
print([{"first_name":item[0],"last_name" :item[1]} for item in itertools.product(*items, repeat=1) if len(item)>1])
output:
[{'first_name': 'Mahmoud', 'last_name': 'Abadi'}, {'first_name': 'Mahmoud', 'last_name': 'Yao'}, {'first_name': 'Pei-chi', 'last_name': 'Abadi'}, {'first_name': 'Pei-chi', 'last_name': 'Yao'}]
I have an arbitrary python dictionary x
such that the values of each key is itself a list. Here is an example:
x = {"first_name": ["Mahmoud", "Pei-chi"], "second_name": ["Abadi", "Yao"]}
Given x
I would like write a method that computes a list of dictionaries such that each dictionary has the same keys as x but the values are each combination of individual list element. So in this case, the result should be:
[{"first_name": "Mahmoud", "second_name": "Abadi"},
{"first_name": "Mahmoud", "second_name": "Yao"},
{"first_name": "Pei-chi", "second_name": "Abadi"},
{"first_name": "Pei-chi", "second_name": "Yao"}]
How can I do it? The dictionary x
may have any arbitrary number of keys with arbitrary names.
You can issue the following list comprehension.
result = [dict(zip(x.keys(), t)) for t in product(*x.values())]
Python’s itertools.product()
is good for this:
from itertools import product
x = {"first_name": ["Mahmoud", "Pei-chi"], "second_name": ["Abadi", "Yao"]}
dict_list = [{'first_name': f, 'second_name':s} for f, s in product(x['first_name'], x['second_name'])]
Giving you:
[{'first_name': 'Mahmoud', 'second_name': 'Abadi'}, {'first_name': 'Mahmoud', 'second_name': 'Yao'}, {'first_name': 'Pei-chi', 'second_name': 'Abadi'}, {'first_name': 'Pei-chi', 'second_name': 'Yao'}]
If you start from basic operations on list and dictionary with this simple for loops you can do above operations, but yeah it’s not as much optimized as of others in terms of neatness, nut complexity of operations is same.
lis = []
for f in x:
if f =='first_name':
for l in x[f]:
for j in x['second_name']:
dic ={}
dic['first-name']=l;dic['last_name']=j
lis.append(dic)
O/P will be as we expect:
[{'first-name': 'Mahmoud', 'last_name': 'Abadi'},
{'first-name': 'Mahmoud', 'last_name': 'Yao'},
{'first-name': 'Pei-chi', 'last_name': 'Abadi'},
{'first-name': 'Pei-chi', 'last_name': 'Yao'}]
If your dict are unordered don’t worry you can use ordered dict , it will not change the result, it will give the same result every time.
dict_1 = {"first_name": ["Mahmoud", "Pei-chi"], "second_name": ["Abadi", "Yao"]}
import itertools
import collections
from operator import itemgetter
ordered_dict=collections.OrderedDict(sorted(dict_1.items(), key=itemgetter(0)))
items=[]
for key,value in ordered_dict.items():
items.append(value)
print([{"first_name":item[0],"last_name" :item[1]} for item in itertools.product(*items, repeat=1) if len(item)>1])
output:
[{'first_name': 'Mahmoud', 'last_name': 'Abadi'}, {'first_name': 'Mahmoud', 'last_name': 'Yao'}, {'first_name': 'Pei-chi', 'last_name': 'Abadi'}, {'first_name': 'Pei-chi', 'last_name': 'Yao'}]