Reputation:
I was working on a problem in which I had to write a program that will count the number of each item needed for the chefs to prepare. The items that a customer can order are: salad, hamburger, and water. salad:[# salad] hamburger:[# hamburger] water:[# water]
for example If order = "hamburger water hamburger"
then the function returns "salad:0 hamburger:2 water:1"
My code is :
def ite(order,item):
v=order.split()
t=[]
for salad in v:
if salad==item:
t.append(item)
v.remove(item)
return len (t)
def item_order(order):
s=ite(order,'salad')
h=ite(order,'hamburger')
w=ite(order,'water')
x='salad:%s hamburger:%s water:%s' %(s,h,w)
return x
but when we give the input item_order('water water water')
, my program prints
salad:0 hamburger:0 water:2
instead of
salad:0 hamburger:0 water:3
It works fine if there are no two consecutive words. How can I correct this?
Upvotes: 3
Views: 774
Reputation: 3741
You shouldn't use t.remove() while iterating on it. But why? I tried to simulate your problem
t = ['a', 'b', 'c']
it = iter(it) # for loop internally use iter() to iterate
it.next() # it should print 1st element of list which is 'a'
t.remove('a') #Now list t becomes ['b', 'c']
it.next() # it should print 2nd element of list which is 'c'
t.remove('b') #Now list t becomes ['c']
it.next() #it should print 3rd element of list, But list has only one element
# It throws exception - "StopIteration"
This exception is handled by "for" loop silently, so "for" loop won't iterate on 3rd element.
Upvotes: 2
Reputation: 777
You shouldn't do
v.remove(salad)
Remove that line and it will solve your problem.
In [18]: def ite(order,item):
v=order.split()
t=[]
for salad in v:
if salad==item:
t.append(item)
return len(t)
In [19]: item_order('salad salad water')
Out[19]: 'salad:2 hamburger:0 water:1'
In [20]: item_order('salad water salad')
Out[20]: 'salad:2 hamburger:0 water:1'
In [21]: item_order('salad water hamburger')
Out[21]: 'salad:1 hamburger:1 water:1'
Reason why it wouldn't work for back to back duplicate elements is, in Python, iterating over a sequence does not implicitly make a copy of the list. The list will be modified. You can make the duplicate copy by v[:],
In [98]: def ite(order,item):
v=order.split()
t=[]
for salad in v[:]:
if salad==item:
t.append(item)
v.remove(item)
return len(t)
Upvotes: 2
Reputation: 85482
You can use collections.Counter:
from collections import Counter
def item_order(order, items=('salad', 'hamburger', 'water')):
counter = Counter(order.split())
return ' '.join(['{}: {}'.format(item, counter.get(item, 0)) for item in items])
print(item_order('water water water'))
print(item_order('water salad, salad'))
print(item_order('water hamburger'))
test it:
print(item_order('water water water'))
print(item_order('water salad, salad'))
print(item_order('water hamburger'))
prints:
salad: 0 hamburger: 0 water: 3
salad: 1 hamburger: 0 water: 1
salad: 0 hamburger: 1 water: 1
The items are given as default parameter:
def item_order(order, items=('salad', 'hamburger', 'water')):
This make the function more flexible because you can hand in other items if desired:
def item_order(order, items=('salad', 'fruit', 'water')):
The use of a tuple is intentional here because mutable default parameters such as a list may cause unintentional side effects. No problem here but could be the vase in general.
After splitting the input string at white spaces into a list, Counter
will create a new counter
instance:
counter = Counter(order.split())
For example:
>>> Counter('water water salad'.split())
Counter({'salad': 1, 'water': 2})
Finally, a list comprehension helps to create anew string:
' '.join(['{}: {}'.format(item, counter.get(item, 0)) for item in items])
The ' '.join
makes a new string form a list of strings, where the list elements are separated by white space. For example:
>>> ' '.join(['abc', 'xyz', 'uvw'])
'abc xyz uvw'
The method get()
of the Python dictionary returns the value for the key if the key is in it, otherwise the default value. For example:
>>> d = {'a': 100, 'b': 200}
>>> d.get('a', 0)
100
>>> d.get('x', 0)
0
Setting this default to 0
, gives a zero count for items not contained in the order:
counter.get(item, 0))
Finally, the format()
method helps to put the value for the count in a string. For example:
>>> '{}: {}'.format('abc', 10)
'abc: 10'
Upvotes: 4
Reputation: 524
Altering a list while iterating over it is generally a bad idea - i.e. have you removed an item python has already iterated over or not?
The list object has a count method that may help!
split_order = order.split(" ")
salads = split_order.count('salad')
https://docs.python.org/2/tutorial/datastructures.html
Upvotes: 0