Michal Habel
Michal Habel

Reputation: 47

Is there a way to zip inner elements of a list/tuple in python?

I've encountered a simple python problem to solve: Having lists of 3 categories of items and their corresponding values, print out all combinations across the 3 lists which have lower total value than X. (Code example might be clearer explanation).

I have managed to solve this using zip() and itertools.product() to create the combos, but when it comes to outputting the right combos, I feel like there has to be a better, more pythonic way to express the output by zipping the inner tuples of the products to get the sum of prices without needing to create 2 explicit genrators/list (the use case does not matter in this case I believe). Coming from a Java background, I'm still having problems at times not lapsing into Java-like syntax which is what I definitely want to avoid with Python.

Here is my code:

import itertools

# Print out all possible meal combinations under $30

main_courses = ['beef stew', 'fried fish']
price_main_courses = [28, 23]

desserts = ['ice-cream', 'cake']
price_desserts = [2, 4]

drinks = ['cola', 'wine']
price_drinks = [3, 10]

products = itertools.product(
    zip(main_courses, price_main_courses),
    zip(desserts, price_desserts),
    zip(drinks, price_drinks)
)

for combo in products:
    names = (x[0] for x in combo)
    price = sum((x[1] for x in combo))
    if price <= 30:
        print(*names, price)

Upvotes: 2

Views: 84

Answers (3)

kederrac
kederrac

Reputation: 17322

you could use a list comprehension

result = [(m, de, dr) for (m, mp), (de, dep), (dr, drp) in products if mp + dep + drp <= 30 ]

or you could manipulate your data structure by using a dict to store the name of your products and the prices instead of using 2 lists:

import itertools

# Print out all possible meal combinations under $30
main_courses_price = {'beef stew': 28, 'fried fish': 23}
desserts_price = {'ice-cream': 2, 'cake': 4}
drinks_price = {'cola': 3, 'wine': 10}

full_prices = {**main_courses_price, **desserts_price, **drinks_price}
products = itertools.product(main_courses_price, desserts_price, drinks_price)


print(*(t for t in products if sum(map(full_prices.get, t)) <= 30), sep='\n')

output:

('fried fish', 'ice-cream', 'cola')
('fried fish', 'cake', 'cola')

Upvotes: 1

marcos
marcos

Reputation: 4510

Mmm, the only different thing that I would do is using list comprehensions instead of the for loop, for example:

import itertools

# Print out all possible meal combinations under $30

main_courses = ['beef stew', 'fried fish']
price_main_courses = [28, 23]

desserts = ['ice-cream', 'cake']
price_desserts = [2, 4]

drinks = ['cola', 'wine']
price_drinks = [3, 10]

products = itertools.product(
    zip(main_courses, price_main_courses),
    zip(desserts, price_desserts),
    zip(drinks, price_drinks)
)

combos = [
    combo for combo in products if sum(price for name, price in combo) <= 30
]

for combo in combos:
    print(' + '.join(name for name, price in combo), '=', sum(price for name, price in combo))

>>> "fried fish + ice-cream + cola = 28"
>>> "fried fish + cake + cola = 30"

Upvotes: 1

Michael Butscher
Michael Butscher

Reputation: 10959

Based on this the for-loop can be written as:

for combo in products:
    names, prices = zip(*combo)
    price = sum(prices)
    if price <= 30:
        print(*names, price)

Upvotes: 2

Related Questions