Getting Started with Collections Module in Python
Tags: programming, python, beginners, collections
Published: 2021-11-11 10:52:03

In This Article we will go through various example covering the data structures **containers** offered by python collections module.

The *collections* module provide us with various different *container* datatypes, which are used to stored objects, and 
access them, iterate over the objects, these all *container* datatypes , all have there usecases, which we will look later in the Article.

Before going forward, i'd like to mention one more point, all the *container* datatypes are built on top of the existing datatypes available in python like *(Tuple,List,Set,Dict)*, these are introduced  to improve the functionalities of the built-in  *container* datatype in python. 
As we progess further in the Article things will get clear to you.

We will going to the look at *containers* available in *collections* module :-


1. *Counters*
2. *OrderedDict*
3. *DefaultDict*
4. *ChainMap*
5. *NamedTuple*
6. *DeQue*


to use *containers* available in *collections* module, you will first have to import them,
there are mutiple ways to import :-

### Example :-
```
#Import all container at once
from collections import *

#Import single container
from collections import Counter

# Importing and accessing using alias
import collections as clns
counter = clns.Counter()
```

#### 1. Counter :-

* A Counter is a sub-class of the dictionary.
* It is used to keep the count of the elements in an iterable
* It is an unordered dictionary where the key represents the element in the iterable and value represents the count of that element in the iterable


Example :- 
```
from collections import Counter

names = ['George', 'Paul', 'George', 'Ringo', 'John', 'John', 'George', 'John', 'Ringo', 'Paul', 'Ringo', 'Ringo', 'George', 'Paul', 'John', 'John', 'John', 'John', 'John', 'Paul']

names_counter = Counter(names)
print(names_counter)

Output :- 
Counter({'John': 8, 'George': 4, 'Paul': 4, 'Ringo': 4})
```

We can see that it return *Counter* object , which we can *cast* to *dict* for further use.

* key as the name
* value as the no. of occourances of that key

Counter *container* has *most_common* method which returns List the n most common elements and their counts from the most common to the least.  If n is None, then list all element counts.

calling  *most_common()* on *names_counter* will return in :-
```
names_counter.most_common()

Output :-
[('John', 8), ('George', 4), ('Paul', 4), ('Ringo', 4)]
```
where the most frequent element with its count  is at the from and least frequent element with its count at the end of the list
to know more about other methods available in *Counter* use can call *help(counter_object)* which will give you *documentation* for *Counter*


#### 2. OrderedDict:-

* A OrderedDict is a sub-class of the dictionary.
* It remember the order in which the keys are inserted.
* A regular *dict* does not preserve the order in which the keys are inserted but an *OrderedDict* maintains the order in which keys are inserted , , , , this is the major difference between a regular *dict* and an *OrderedDict*

Example :- 
```
# Regular dict
reg_dict = dict()
reg_dict['apples']  = 3
reg_dict['pineapple'] = 5
reg_dict['mango'] = 1
reg_dict['orange'] = 7
reg_dict['grapes'] = 2
print(f"Regular Dict {reg_dict} \n\n")

from collections import OrderedDict

# Ordered Dict
ord_dict = OrderedDict()
ord_dict['apples']  = 3
ord_dict['pineapple'] = 5
ord_dict['mango'] = 1
ord_dict['orange'] = 7
ord_dict['grapes'] = 2
print(f"Ordered Dict {ord_dict}")

Regular Dict {'apples': 3, 'pineapple': 5, 'mango': 1, 'orange': 7, 'grapes': 2} 


Ordered Dict OrderedDict([('apples', 3), ('pineapple', 5), ('mango', 1), ('orange', 7), ('grapes', 2)])
```

Deleting elment from *OrderedDict* :-
```
del ord_dict['mango']
print(ord_dict)

Output :-
OrderedDict([('apples', 3), ('pineapple', 5), ('orange', 7), ('grapes', 2)])


```

Inserting element in *OrderedDict* :-
```
ord_dict['mango'] = 11
print(ord_dict)

Output :-
OrderedDict([('apples', 3), ('pineapple', 5), ('orange', 7), ('grapes', 2), ('mango', 11)])
```
As we can see first we deleted the key *mango* from *OrderedDict* then re-inserted it at the end of the *OrderedDict*.
for more method use can call *help(OrderedDict_object)*


#### 3. DefaultDict :-

* A DefaultDict is a sub-class of the dictionary.
* The functionality of both dictionaries and defualtdict are almost same except for the fact that defualtdict never raises a *KeyError*.
* It provides a default value for the key that does not exists.
* We can pass a default values while initialising the DefaultDict as *DefaultDict(default_value=None)
* It can take any type of default value

Example :- 
```
 
from collections import defaultdict

d_dict = defaultdict(lambda : "Not Available") # passing default value

d_dict['John'] = 11
d_dict['Paul'] = 12
d_dict['George'] = 9
d_dict['Rango'] = 14

print(d_dict['Rango'])
print(d_dict['John'])
print(d_dict['max'])

Output :-
14
11
Not Available
```


#### 4. ChainMap :-

* A ChainMap is a *container* which stores many *dictionaries* into single *ChainMap* object.
* We can perform operations on *ChainMap* object using following methods :-
1. keys() :- This function is used to display all the keys of all the dictionaries in ChainMap.

2. values() :- This function is used to display values of all the dictionaries in ChainMap.

3. maps() :- This function is used to display keys with corresponding values of all the dictionaries in ChainMap.

Example :- 
```
from collections import ChainMap  
       
       
d1 = {'Ram': 12, 'Shyam': 12} 
d2 = {'Mango': 3, 'Orange': 4} 
d3 = {'Car': 4, 'Bus': 8} 
    
# Defining the chainmap  
c = ChainMap(d1, d2, d3)  
       
print(c)

Output :- 
ChainMap({'Ram': 12, 'Shyam': 12}, {'Mango': 3, 'Orange': 4}, {'Car': 4, 'Bus': 8})

```

```
  
# printing chainMap using maps
print ("All the ChainMap contents are : ")
print (c.maps)
  
# printing keys using keys()
print ("All keys of ChainMap are : ")
print (list(c.keys()))
  
# printing keys using keys()
print ("All values of ChainMap are : ")
print (list(c.values()))

Output :-

All the ChainMap contents are : 
[{'Ram': 12, 'Shyam': 12}, {'Mango': 3, 'Orange': 4}, {'Car': 4, 'Bus': 8}]
All keys of ChainMap are : 
['Car', 'Bus', 'Mango', 'Orange', 'Ram', 'Shyam']
All values of ChainMap are : 
[4, 8, 3, 4, 12, 12]
```


#### 5. NamedTuple:-

* It contain keys that are hashed to a particular value.
* It supports both access from key-value and iteration.
* following methods are availble in *namedtuple* to access the value :-

1. access by index:  we can pass index within square brackets to  access the value ,unlike dictionaries which are not accessible by index.
2. access by keyname: access value by using *.* operator followed by name of the attribute.
3. access using getattr(): This is another way to access the value by giving namedtuple and key value as its argument.  


Example :- 
```
from collections import namedtuple

Book = namedtuple('Book', ['title', 'author', 'price'])

b = Book('Xyz', 'xyz', 50)

print("accessing using index")
print(b[0])
print(b[1])
print(b[2])

print("\n------------------\n")

print("accessing using name")
print(b.title)
print(b.author)
print(b.price)

print("\n------------------\n")

print("using getattr() ")
print(getattr(b, 'title'))
print(getattr(b, 'author'))
print(getattr(b, 'price'))

Output :-
accessing using index
Xyz
xyz
50

------------------

accessing using name
Xyz
xyz
50

------------------

using getattr() 
Xyz
xyz
50
```
  

#### 6. DeQue:-

* *Deque* (Doubly Ended Queue) in Python is implementation available in *collections* module
* *Deque* helps in fast insertion and deletion operation which 
  *list* datatypes lacks 
* *Deque* offers insertion and deletion in *O(1) time complexity*
  whereas *list* offer insetion and deletion in *O(n) time complexity*.
* It offers operations :-

1. append() :- This function is used to insert the value in its argument to the right end of deque.
2. appendleft() :- This function is used to insert the value in its argument to the left end of deque.
3. pop() :- This function is used to delete an argument from the right end of deque.
4. popleft() :- This function is used to delete an argument from the left end of deque.
these are the frequenty used methods, you can find other method by calling *help(deque_object)*.


Example :- 
```
from collections import deque

# initializing deque
dq = deque(['John','Max','Harry','Marcus','Robin','Jackub'])

# using append() to insert element at right end
dq.append("Jimmy")

print ("The deque after appending at right is : ")
print (dq,"\n")

# using appendleft() to insert element at left end
dq.appendleft('Alex')
print ("The deque after appending at left is : ")
print (dq,"\n")

# using pop() to delete element from right end
dq.pop()
print ("The deque after deleting from right is : ")
print (dq,"\n")

# using popleft() to delete element from left end
dq.popleft()
print ("The deque after deleting from left is : ")
print (dq,"\n")

Output :-

The deque after appending at right is : 
deque(['John', 'Max', 'Harry', 'Marcus', 'Robin', 'Jackub', 'Jimmy']) 

The deque after appending at left is : 
deque(['Alex', 'John', 'Max', 'Harry', 'Marcus', 'Robin', 'Jackub', 'Jimmy']) 

The deque after deleting from right is : 
deque(['Alex', 'John', 'Max', 'Harry', 'Marcus', 'Robin', 'Jackub']) 

The deque after deleting from left is : 
deque(['John', 'Max', 'Harry', 'Marcus', 'Robin', 'Jackub']) 

```


We have completed 6  *containers* available in  the *collections* module , there are other *container* datatype available in *collections* module you can check by clicking the link below :-
[other collections module *containers*](https://docs.python.org/3/library/collections.html)

Hope you guys have learned all the topics covered in this Article :-)