Tony
Tony

Reputation: 3638

itertools all possible combinations with multiple elements

I'm trying to find a way to generate ALL possible combinations using the categories below. I need exactly one element from each of Type, Cutlery, IsWeekend, and at least one from FoodTypes. Within those parameters, I want to generate all possible combinations of choices.

options = {
'Type' : ['Breakfast', 'Brunch', 'Lunch', 'Dinner'],
'Cutlery': ['Knife', 'Fork'],
'IsWeekend' : ['True', 'False'],
'FoodTypes' : ['Sausage', 'Bacon', 'Eggs', 'Toast']
}

For example.

Breakfast, Knife, True, Sausage
Breakfast, Knife, True, Sausage, Bacon
....
Breakfast, Fork, False, Sasuage, Eggs, Toast

I've been playing around with itertools using product and combinations, but I can't seem to get the right recipe. It's currently producing a list of products, but using only one element from each list, such as:

Breakfast, Knife, True, Sausage
Breakfast, Knife, True, Bacon

How can I extend this to cover multiple food choices?

Upvotes: 2

Views: 2900

Answers (2)

game0ver
game0ver

Reputation: 1290

You can do this using itertools.combinations like so:

from itertools import combinations

options = {
    'Type':       ['Breakfast', 'Brunch', 'Lunch', 'Dinner'],
    'Cutlery':    ['Knife', 'Fork'],
    'IsWeekend' : ['True', 'False'],
    'FoodTypes' : ['Sausage', 'Bacon', 'Eggs', 'Toast']
}

# get a list of all the dictionary's values
lst = [j for i in options.values() for j in i]

for i in range(len(lst)+1):
    for s in combinations(lst, i): print(s)

Note: This solution computes all 2^12 combinations of the flattened list of dictionary's values and works supposing that the dictionary doesn't contain duplicate items in its values. If there are duplicates, before you enter the for loops you can easily remove them e.g. using sets.

Upvotes: 2

Prune
Prune

Reputation: 77910

From your examples, it appears that you want the power set of the food items, and then all combinations of one element of each other entry with that set. I include the powerset given in the Python itertools documentation.

from itertools import *

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

options = {
    'Type' : ['Breakfast', 'Brunch', 'Lunch', 'Dinner'],
    'Cutlery': ['Knife', 'Fork'],
    'IsWeekend' : ['True', 'False'],
    'FoodTypes' : ['Sausage', 'Bacon', 'Eggs', 'Toast']
}

menu = powerset(options['FoodTypes'])

for setting in product(
    options['Type'],
    options['Cutlery'],
    options['IsWeekend'],
    menu ):

    print(setting)

Output is below; I trust that you can flatten the list and remove the empty menu item (i.e. left as an exercise for the reader).

('Breakfast', 'Knife', 'True', ())
('Breakfast', 'Knife', 'True', ('Sausage',))
('Breakfast', 'Knife', 'True', ('Bacon',))
('Breakfast', 'Knife', 'True', ('Eggs',))
('Breakfast', 'Knife', 'True', ('Toast',))
('Breakfast', 'Knife', 'True', ('Sausage', 'Bacon'))
('Breakfast', 'Knife', 'True', ('Sausage', 'Eggs'))
('Breakfast', 'Knife', 'True', ('Sausage', 'Toast'))
('Breakfast', 'Knife', 'True', ('Bacon', 'Eggs'))
('Breakfast', 'Knife', 'True', ('Bacon', 'Toast'))
('Breakfast', 'Knife', 'True', ('Eggs', 'Toast'))
('Breakfast', 'Knife', 'True', ('Sausage', 'Bacon', 'Eggs'))
('Breakfast', 'Knife', 'True', ('Sausage', 'Bacon', 'Toast'))
('Breakfast', 'Knife', 'True', ('Sausage', 'Eggs', 'Toast'))
('Breakfast', 'Knife', 'True', ('Bacon', 'Eggs', 'Toast'))
('Breakfast', 'Knife', 'True', ('Sausage', 'Bacon', 'Eggs', 'Toast'))
('Breakfast', 'Knife', 'False', ())
('Breakfast', 'Knife', 'False', ('Sausage',))
('Breakfast', 'Knife', 'False', ('Bacon',))
('Breakfast', 'Knife', 'False', ('Eggs',))
('Breakfast', 'Knife', 'False', ('Toast',))
...
('Dinner', 'Fork', 'False', ('Sausage', 'Eggs', 'Toast'))
('Dinner', 'Fork', 'False', ('Bacon', 'Eggs', 'Toast'))
('Dinner', 'Fork', 'False', ('Sausage', 'Bacon', 'Eggs', 'Toast'))

Upvotes: 3

Related Questions