Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Counting and grouping in Python

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?


2 Answers

Solution

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

Explanation

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'
like image 87
Mike Müller Avatar answered Feb 23 '26 02:02

Mike Müller


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)
like image 30
Sagar Waghmode Avatar answered Feb 23 '26 04:02

Sagar Waghmode



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!