Zip the good and bad lists, map each resulting tuple to a set and feed that to itertools.product()
:
from itertools import product
for combo in product(*map(set, zip(good, bad))):
print(combo)
Demo:
>>> good = (0,1,2,3)
>>> bad = (0,10,20,3)
>>> from itertools import product
>>> for combo in product(*map(set, zip(good, bad))):
... print(combo)
...
(0, 1, 2, 3)
(0, 1, 20, 3)
(0, 10, 2, 3)
(0, 10, 20, 3)
This will take any size of inputs; not just good
and bad
, but you can add in ugly
as well:
>>> ugly = (1,2,4,3)
>>> for combo in product(*map(set, zip(good, bad, ugly))):
... print(combo)
...
(0, 1, 4, 3)
(0, 1, 2, 3)
(0, 1, 20, 3)
(0, 10, 4, 3)
(0, 10, 2, 3)
(0, 10, 20, 3)
(0, 2, 4, 3)
(0, 2, 2, 3)
(0, 2, 20, 3)
(1, 1, 4, 3)
(1, 1, 2, 3)
(1, 1, 20, 3)
(1, 10, 4, 3)
(1, 10, 2, 3)
(1, 10, 20, 3)
(1, 2, 4, 3)
(1, 2, 2, 3)
(1, 2, 20, 3)
Generalized to a function:
def op(*sequences):
return product(*map(set, zip(*sequences)))
for combo in op(good, bad):
print(combo)
for combo in op(good, bad, ugly):
print(combo)
Because a set
is used to produce unique values from each combined set of inputs, the output order is not the same as the order of the inputs. If order is important, you can replace set
with a dupe-removing order-preserving function instead:
def unique_with_order(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if x not in seen and not seen_add(x)]
def ordered_op(*sequences):
return product(*map(unique_with_order, zip(*sequences)))
which produces output ordered according to the order of the inputs:
>>> for combo in ordered_op(good, bad):
... print(combo)
...
(0, 1, 2, 3)
(0, 1, 20, 3)
(0, 10, 2, 3)
(0, 10, 20, 3)
>>> for combo in ordered_op(bad, good):
... print(combo)
...
(0, 10, 20, 3)
(0, 10, 2, 3)
(0, 1, 20, 3)
(0, 1, 2, 3)