Reputation: 4071
I'm learning Python (and programming in general) by making small programs. Below is a basic shopping program which will return a list of items to buy based on the selected food.
I'd like to improve it and allow user to select several foods at once (e.g. user input would be "1, 2, 3") and return a list of ingredients based on that.
What kind of approach should I take? I'm using Python 2.7 and here is my existing code:
mm_soup = ['minced meat', 'potatoes', 'frozen vegetable']
sunday_soup = ['chicken with bones', 'noodles', 'soup vegetable']
gulas = ['pork meat', 'food cream', 'potatoes', 'onion', 'frozen peas']
print "What would you like to cook on weekend?"
print "Here are the options:"
print "1. Minced Meat Soup"
print "2. Sunday Soup"
print "3. Gulas"
choose = raw_input("> ")
if choose == '1':
print "Buy", ", ".join(mm_soup) + "."
elif choose == '2':
print "Buy", ", ".join(sunday_soup) + "."
elif choose == '3':
print "Buy", ", ".join(gulas) + "."
else:
print "Hmmm. No such food on the list."
Upvotes: 2
Views: 9856
Reputation: 89077
There are some common problems with your code, so let's begin by fixing those.
You have multiple items you want to present at the user, and you are hard-coding those values. This makes a lot of effort for you as you have to repeat yourself a lot. Look at your choice lines, they all come to basically the same thing. You also repeat yourself by defining what the numbers link to in your description, and in your code. Let's try to simplify this with a data structure.
Here, we make a list of all the options - a list of tuples, defining the name and set of items for the given food. We use a set here as we don't need to order the items.
options = [
("Minced Meat Soup", {'minced meat', 'potatoes', 'frozen vegetable'}),
("Sunday Soup", {'chicken with bones', 'noodles', 'soup vegetable'}),
("Gulas", {'pork meat', 'food cream', 'potatoes', 'onion', 'frozen peas'}),
]
This gives us a good data structure to begin with.
We can then make our question, rather than manually constructing it, we can construct it from our list of options using a loop:
print "What would you like to cook on weekend?"
print "Here are the options:"
for option, (name, values) in enumerate(options, 1):
print str(option)+". "+name
Note the use of the enumerate()
builtin in order to give us the numbers for the options. As you want to start from 1, and Python counts from 0 normally, we pass that in too.
This gives us our output, but we can now easily add more items without modifying the existing code. We ask like before, and then instead of a load of if
/elif
s, we can simply get the index they give us from the list. We first have to change the string to a number, and then take away one (as Python counts from 0). This gives us:
_, values = options[int(choose)-1]
(Using tuple unpacking to ignore the first value, as it's the name, which we don't need).
The only issue now is what will happen if the user types in a number out of range or an word instead, for example. You could check it before you convert to an int and use it, but it's Pythonic to simply try it, and catch the thrown exceptions upon failure. For example:
try:
_, values = options[int(choose)-1]
print "Buy", ", ".join(values) + "."
except (IndexError, ValueError):
print "Hmmm. No such food on the list."
This makes the entire program much smaller, and also note how easy it is to add new items, you simply add them to the list.
So how do we deal with multiple items? Well, that's pretty simple now too. We can take the user's input, split in on commas, and strip the values to remove any spaces, then do the same thing we did before:
for choice in choose.split(","):
choice = choice.strip()
try:
_, values = options[int(choice)-1]
print "Buy", ", ".join(values) + "."
except (IndexError, ValueError):
print "Hmmm. No such food on the list."
This works, printing out multiple buy lines, but it's not optimal, a better idea would be to produce one bigger shopping list containing all of the needed items.
We can build this by building up a set of all of the items as we loop, then printing out that set.
shopping_list = []
for choice in choose.split(","):
choice = choice.strip()
try:
_, values = options[int(choice)-1]
shopping_list.append(values)
except (IndexError, ValueError):
print "Hmmm. No such food on the list."
This is a little inefficient and ugly, however. Python has some built in functionality to build up lists - list comprehensions. We can do this operation like so:
try:
shopping_list = [options[int(choice.strip())-1][3] for choice in choose.split(",")]
except (IndexError, ValueError):
print "Hmmm. No such food on the list."
Now we need to print out all the values in the list. Remember that this is a list of sets, so ", ".join()
won't do exactly what we want. We have two options here. We can either use a generator expression to join the sets first, then join the joined strings:
print "Buy " + ", ".join(", ".join(values) for values in shopping_list) + "."
Or, we could use itertools.chain.from_iterable()
to return a flattened iterator:
print "Buy " + ", ".join(itertools.chain.from_iterable(shopping_list)) + "."
This gives us:
import itertools
options = [
("Minced Meat Soup", {'minced meat', 'potatoes', 'frozen vegetable'}),
("Sunday Soup", {'chicken with bones', 'noodles', 'soup vegetable'}),
("Gulas", {'pork meat', 'food cream', 'potatoes', 'onion', 'frozen peas'}),
]
print "What would you like to cook on weekend?"
print "Here are the options:"
for option, (name, values) in enumerate(options, 1):
print str(option)+". "+name
choose = raw_input("> ")
try:
shopping_list = [options[int(choice.strip())-1][1] for choice in choose.split(",")]
print "Buy " + ", ".join(itertools.chain.from_iterable(shopping_list)) + "."
except (IndexError, ValueError):
print "Hmmm. No such food on the list."
Which produces something like:
What would you like to cook on weekend?
Here are the options:
1. Minced Meat Soup
2. Sunday Soup
3. Gulas
> 1, 2
Buy potatoes, frozen vegetable, minced meat, chicken with bones, noodles, soup vegetable.
This is short, easy to expand on, and functions well. There are some other issues you could deal with (how do you want to handle multiple of the same item? You might want to look into collections.Counter
for that), but it's the basic idea.
Upvotes: 8
Reputation: 11264
I suppose you could just split the entered string and iterate through its elements:
choose = raw_input("> ")
for choice in choose.split(', '): #split by comma and space
if choice == '1':
print "Buy", ", ".join(mm_soup) + "."
elif choice == '2':
print "Buy", ", ".join(sunday_soup) + "."
elif choice == '3':
print "Buy", ", ".join(gulas) + "."
else:
print "Hmmm. No such food on the list."
Or something like that:
choose = raw_input("> ");
selected = choose.split(', ') #split by comma and space
suggestion = '';
if '1' in selected :
suggestion += ", ".join(mm_soup)
if '2' in selected :
suggestion += ", ".join(sunday_soup)
if '3' in selected :
suggestion += ", ".join(gulas)
if len(suggestion) > 0:
print "Buy " + suggestion + "."
else:
print "Hmmm. No such food on the list."
Upvotes: 1