Given a dictionary like this with some items being tuples...
params = {
'a': 'static',
'b': (1, 2),
'c': ('X', 'Y')
}
I need the "product" of the items into a list of dict like this, with the tuples expanded so each item in b will be matched with each item in c...
[{ 'a': 'static', 'b': 1, 'c': 'X' },
{ 'a': 'static', 'b': 1, 'c': 'Y' },
{ 'a': 'static', 'b': 2, 'c': 'X' },
{ 'a': 'static', 'b': 2, 'c': 'Y')}]
I can easily separate the initial input into a list of non-tuple items and tuple items, and apply the key of each tuple to the values as a "tag" prior to multiplication so they look like this: 'b##1', 'b##2', 'c##X', 'c##Y'
. Then parse those back into the above dict after multiplication. If I would always see 2 tuple items (like b and c), I could easily pass both to itertools.products
. But there could be 0..n tuple items, and product()
doesn't multiply a list of lists in this way. Can anyone think of a solution?
TAG = '##'
# separate tuples and non-tuples from the input, and prepend the key of each tuple as a tag on the value to parse out later
for key, value in params.items():
if type(value) is tuple:
for x in value:
tuples.append(f'{key}{TAG}{x}')
else:
non_tuples.append({key: value})
print(list(product(tuples)) # BUG: doesn't distribute each value of b with each value of c
product
takes multiple iterables, but the key thing to remember is that an iterable can contain a single item. In cases where a value in your original dict isn't a tuple (or maybe a list), you want to convert it to a tuple containing a single value and pass that to product
:
params_iterables = {}
for k, v in params.items():
if isinstance(v, (tuple, list)):
params_iterables[k] = v # v is already a tuple or a list
else:
params_iterables[k] = (v, ) # A tuple containing a single value, v
which gives:
params_iterables = {'a': ('static',), 'b': (1, 2), 'c': ('X', 'Y')}
Then, simply get the product of the values in params_iterables
:
result = []
for values in product(*params_iterables.values()):
result.append(dict(zip(params, values)))
The dict(zip(params, values))
line creates a dict where the first element of values
is assigned the first key in params
, and so on. This dict is then appended to result
, which gives the desired output:
[{'a': 'static', 'b': 1, 'c': 'X'},
{'a': 'static', 'b': 1, 'c': 'Y'},
{'a': 'static', 'b': 2, 'c': 'X'},
{'a': 'static', 'b': 2, 'c': 'Y'}]