Counting and grouping in Python
Question: 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?


Answer: ## Solution

You can use
[collections.Counter](https://docs.python.org/3/library/collections.html?highlight=counter#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'
    

