Explode a dict - Get all combinations of the values in a dictionary

9.5k views Asked by At

I want to get all combinations of the values in a dictionary as multiple dictionaries (each containing every key of the original but only one value of the original values). Say I want to parametrize a function call with:

kwargs = {'a': [1, 2, 3], 'b': [1, 2, 3]}

How do I get a list of all the combinations like this:

combinations = [{'a': 1, 'b': 1}, {'a': 1, 'b': 2}, {'a': 1, 'b': 3},
                {'a': 2, 'b': 1}, {'a': 2, 'b': 2}, {'a': 2, 'b': 3},
                {'a': 3, 'b': 1}, {'a': 3, 'b': 2}, {'a': 3, 'b': 3}]

There can be an arbitary amount of keys in the original kwargs and each value is garantueed to be an iterable but the number of values is not fixed.

If possible: the final combinations should be a generator (not a list).

2

There are 2 answers

0
thefourtheye On BEST ANSWER

You can flatten the kwargs to something like this

>>> kwargs = {'a': [1, 2, 3], 'b': [1, 2, 3]}
>>> flat = [[(k, v) for v in vs] for k, vs in kwargs.items()]
>>> flat
[[('b', 1), ('b', 2), ('b', 3)], [('a', 1), ('a', 2), ('a', 3)]]

Then, you can use itertools.product like this

>>> from itertools import product
>>> [dict(items) for items in product(*flat)]
[{'a': 1, 'b': 1},
 {'a': 2, 'b': 1},
 {'a': 3, 'b': 1},
 {'a': 1, 'b': 2},
 {'a': 2, 'b': 2},
 {'a': 3, 'b': 2},
 {'a': 1, 'b': 3},
 {'a': 2, 'b': 3},
 {'a': 3, 'b': 3}]

itertools.product actually returns an iterator. So you can get the values on demand and build your dictionaries. Or you can use map, which also returns an iterator.

>>> for item in map(dict, product(*flat)):
...     print(item)
...
...
{'b': 1, 'a': 1}
{'b': 1, 'a': 2}
{'b': 1, 'a': 3}
{'b': 2, 'a': 1}
{'b': 2, 'a': 2}
{'b': 2, 'a': 3}
{'b': 3, 'a': 1}
{'b': 3, 'a': 2}
{'b': 3, 'a': 3}
3
Stefan Pochmann On

Just another way, building the value tuples first and then combining with keys afterwards (pretty much the opposite of @thefourtheye's way :-).

>>> combinations = (dict(zip(kwargs, vs)) for vs in product(*kwargs.values()))
>>> for c in combinations:
        print(c)

{'a': 1, 'b': 1}
{'a': 1, 'b': 2}
{'a': 1, 'b': 3}
{'a': 2, 'b': 1}
{'a': 2, 'b': 2}
{'a': 2, 'b': 3}
{'a': 3, 'b': 1}
{'a': 3, 'b': 2}
{'a': 3, 'b': 3}