Dictionaries 3 - Methods

Download exercise zip

Browse online files

In this tutorial we will see the main methods to retrieve stuff from dictionaries and to manipulate them, along with some special classes.

Methods:

Method

Return

Description

dict.keys()

dict_keys

Return a view of keys which are present in the dictionary

dict.values()

dict_values

Return a view of values which are present in the dictionary

dict.items()

dict_items

Return a view of (key/value) couples present in the dictionary

d1.update(d2)

None

MODIFY the dictionary d1 with the key / value couples found in d2

Classes:

Class

Description

OrderedDict

Dictionary which allows to maintain the order of insertion of keys

Counter

Dictionary which allows to rapidly calculate histograms

What to do

  1. Unzip exercises zip in a folder, you should obtain something like this:

sets
    dictionaries1.ipynb
    dictionaries1-sol.ipynb
    dictionaries2.ipynb
    dictionaries2-sol.ipynb
    dictionaries3.ipynb
    dictionaries3-sol.ipynb
    dictionaries4.ipynb
    dictionaries4-sol.ipynb
    dictionaries5.ipynb
    dictionaries5-sol.ipynb
    jupman.py

WARNING: to correctly visualize the notebook, it MUST be in an unzipped folder !

  1. open Jupyter Notebook from that folder. Two things should open, first a console and then a browser. The browser should show a file list: navigate the list and open the notebook dictionaries3.ipynb

  2. Go on reading the exercises file, sometimes you will find paragraphs marked Exercises graded from ✪ to ✪✪✪✪ which will ask to write Python commands in the following cells.

Shortcut keys:

  • to execute Python code inside a Jupyter cell, press Control + Enter

  • to execute Python code inside a Jupyter cell AND select next cell, press Shift + Enter

  • to execute Python code inside a Jupyter cell AND a create a new cell aftwerwards, press Alt + Enter

  • If the notebooks look stuck, try to select Kernel -> Restart

keys method

By calling the method .keys() we can obtain the dictionary keys:

[2]:
vegetables = {'carrots'  : 5,
              'tomatoes' : 8,
              'cabbage'  : 3}
[3]:
vegetables.keys()
[3]:
dict_keys(['carrots', 'cabbage', 'tomatoes'])

WARNING: THE RETURNED SEQUENCE IS OF TYPE dict_keys

dict_keys might look like a list but it is well different!

In particular, the returned sequence dict_keys is a view on the original dictionary. In computer science, when we talk about views we typically intend collections which contain a part of the objects contained in another collection, and if the original collection gets modified, so is the view at the same time.

Let’s see what this means. First let’s assign the sequence of keys to a variable:

[4]:
ks = vegetables.keys()

Then we modify the original dictionary, adding an association:

[5]:
vegetables['potatoes'] = 8

If we now print ks, we should see the change:

[6]:
ks
[6]:
dict_keys(['carrots', 'potatoes', 'cabbage', 'tomatoes'])

Sequence returned by .keys() can change over time!

When reusing the sequence from .keys(), ask yourself if the dictionary could have changed in the meanwhile

If we want a stable version as a sort of static ‘picture’ of dictionary keys at a given moment in time, we must explicitly convert them to another sequence, like for example a list:

[7]:
as_list = list(vegetables.keys())
[8]:
as_list
[8]:
['carrots', 'potatoes', 'cabbage', 'tomatoes']
[9]:
vegetables['cocumber'] = 9
[10]:
as_list     # no cocumbers
[10]:
['carrots', 'potatoes', 'cabbage', 'tomatoes']

Let’s see again the example in Python Tutor:

[11]:
# WARNING: FOR PYTHON TUTOR TO WORK, REMEMBER TO EXECUTE THIS CELL with Shift+Enter
#          (it's sufficient to execute it only once)

import jupman
[12]:
vegetables = {'carrots'  : 5,
              'tomatoes' : 8,
              'cabbage'  : 3}
keys = vegetables.keys()
vegetables['potatoes'] = 8
as_list = list(vegetables.keys())
vegetables['cocumbers'] = 9
#print(as_list)

jupman.pytut()
[12]:

WARNING: WE CAN’T USE INDEXES WITH dict_keys

If we try, we will obtain an error:

>>> vegetables = {'carrots'  : 5,
                  'tomatoes' : 8,
                  'cabbage'  : 3}
>>> ks = vegetables.keys()
>>> ks[0]

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-90-c888bf602918> in <module>()
----> 1 keys[0]

TypeError: 'dict_keys' object does not support indexing

WARNING: WE CANNOT DIRECTLY MODIFY dict_keys

There aren’t operations nor methods which allow us to change the elements of dict_keys, you can only act on the original dictionary.

QUESTION: Look at the following code fragments, and for each try guessing if it can work (or if it gives an error):

  1. diz = {'a':4,
           'b':5}
    
    ks = diz.keys()
    ks.append('c')
    
  2. diz = {'a':4,
           'b':5}
    
    ks = diz.keys()
    ks.add('c')
    
  3. diz = {'a':4,
           'b':5}
    
    ks = diz.keys()
    ks['c'] = 3
    
Show answer

QUESTION: Look at the following code fragments, and for each try guessing which result it produces (or if it gives an error):

  1. diz = {'a':1,'b':2}
    s = set(diz.keys())
    s.add(('c',3))
    print(diz)
    print(s)
    
  2. diz = {'a':3,'b':4}
    k = diz.keys()
    diz['c'] = 5
    print(len(k))
    
  3. diz = {'a':'x',
           'b':'y'}
    print('a' in diz.keys())
    
  4. diz1 = {'a':1,'b':2}
    chiavi = diz1.keys()
    diz2 = dict(diz1)
    diz2['c'] = 3
    print('diz1=',diz1)
    print('diz2=',diz2)
    print('chiavi=',chiavi)
    
  5. diz1 = {'a':'b','c':'d'}
    diz2 = {'a':'b','b':'c'}
    print( set(diz1.keys()) - set(diz2.keys())  )
    
  6. diz1 = {'a':'b','c':'d'}
    diz2 = {'e':'a','f':'c'}
    ks = diz1.keys()
    del diz1[diz2['e']]
    del diz1[diz2['f']]
    print(len(ks))
    

Exercise - messy keys

✪ PRINT a LIST with all the keys in the dictionary

  • NOTE 1: it is NOT necessary for the list to be sorted

  • NOTE 2: to convert any sequence to a list, use the predefined function list

Show solution
[13]:
d = {'c':6, 'b':2,'a':5}

# write here


[13]:
['b', 'c', 'a']

Exercise - sorted keys

✪ PRINT a LIST with all the dictionary keys

  • NOTE 1: Now it IS necessary for the list to be sorted

  • NOTE 2: to convert any sequence to a list, use the predefined function list

Show solution
[14]:
d = {'c':6, 'b':2,'a':5}

# write here


['a', 'b', 'c']

Exercise - keyring

Given the dictionaries d1 and d2, write some code which puts into a list ks all the keys in the two dictionaries, without duplicates and alphabetically sorted, and finally prints the list.

  • your code must work with any d1 and d2

Example - given:

d1 = {
    'a':5,
    'b':9,
    'e':2,
}
d2 = {'a':9,
      'c':2,
      'e':2,
      'f':6}

after your code, it must result:

>>> print(keys)
['a', 'b', 'c', 'e', 'f']
Show solution
[15]:
d1 = {
    'a':5,
    'b':9,
    'e':2,
}
d2 = {'a':9,
      'c':2,
      'e':2,
      'f':6}

# write here


values method

Given a dictionary, we can obtain all the values by calling the method .values()

Imagine we have a dictionary vehicles which assigns an owner to each car plate:

[16]:
vehicles = {
    'AA111AA' : 'Mario',
    'BB222BB' : 'Lidia',
    'CC333CC' : 'Mario',
    'DD444DD' : 'Gino',
    'EE555EE' : 'Gino'
}

owners = vehicles.values()

WARNING: THE RETURNED SEQUENCE IS OF TYPE dict_values

dict_values may seem a list but it’s not!

We’ve seen dict_keys is a view on the original dictionary, and so is dict_values, thus by adding an association to vehicles

[17]:
vehicles['FF666FF'] = 'Paola'

… the view owners will automatically result changed:

[18]:
owners
[18]:
dict_values(['Lidia', 'Mario', 'Gino', 'Paola', 'Mario', 'Gino'])

We also note that being values of a dictionary, duplicates are allowed.

WARNING: WE CANNOT USE INDEXES WITH dict_values

If we try, we will get an error:

>>> owners[0]

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-90-c888bf602918> in <module>()
----> 1 owners[0]

TypeError: 'dict_values' object does not support indexing

WARNING: WE CANNOT DIRECTLY MODIFY dict_values

There aren’t operations nor methods that allow us to change the elements of dict_values, we can only act on the original dictionary.

QUESTION: Look at the following code fragments, and for each try guessing the result it produces (or if it gives an error):

  1. diz = {'a':4,
           'b':5}
    
    vals = diz.values()
    vals.append(4)
    
  2. d = {0:'a',
         1:'b',
         2:'b'}
    vals = d.values()
    d[2]='c'
    print(vals)
    
  3. diz = {'a':4,
           'b':5}
    
    vals = diz.values()
    vals.add(5)
    
  4. diz = {0:1,
           1:2,
           2:3}
    
    diz[list(diz.values())[0]-1]
    
  5. diz = {'a':4,
           'b':5}
    
    vals = diz.values()
    vals['c'] = 6
    
  6. diz = {'a':4,
           'b':5}
    
    vals = diz.values()
    vals[6] = 'c'
    

Exercise - one by one

Given a dictionary my_dict, write some code which prints True if each key is associated to a value different from the values of all other keys. Otherwise prints False.

Example 1 - given:

my_dict = {'a' : 3,
           'c' : 6,
           'g' : 8}

After your code, it must print True (because 3,6 and 8 are all different)

True

Example 2 - given:

my_dict = {'x' : 5,
           'y' : 7,
           'z' : 5}

it must print:

False
Show solution
[19]:
my_dict = {'a' : 3,
           'c' : 6,
           'g' : 8}

"""
my_dict= {'x' : 5,
          'y' : 7,
          'z' : 5}
"""

# write here


True

Exercise - bag

Given a dictionary my_dict of character associations, write some code which puts into the variable bag the sorted list of all the keys and values.

Example - given:

my_dict = {
    'a':'b',
    'b':'f',
    'c':'b',
    'd':'e'
}

After your code, it must print:

>>> print(bag)
['a', 'b', 'c', 'd', 'e', 'f']
Show solution
[20]:
my_dict = {
    'a':'b',
    'b':'f',
    'c':'b',
    'd':'e'
}

# write here


['a', 'b', 'c', 'd', 'e', 'f']

Exercise - common values

Given two dictionaries d1 and d2, write some code which PRINTS True if they have at least a value in common (without considering the keys)

Example 1 - given:

d1 = {
    'a':4,
    'k':2,
    'm':5
}

d2 = {
    'b':2,
    'e':4,
    'g':9,
    'h':1
}

after your code, it must print True (because they have the values 2 and 4 in common):

Common values? True

Example 2 - given:

d1 = {
    'd':1,
    'e':2,
    'f':6
}

d2 = {
    'a':3,
    'b':5,
    'c':9,
    'd':7
}

after your code, it must print:

Common values? False
Show solution
[21]:
d1 = {
    'a':4,
    'k':2,
    'm':5
}

d2 = {
    'b':2,
    'e':4,
    'g':9,
    'h':1
}

"""
d1 = {
    'd':1,
    'e':2,
    'f':6
}

d2 = {
    'a':3,
    'b':5,
    'c':9,
    'd':7
}
"""

# write here


Common values? True

Exercise - small big

Given a dictionary d which has integers as keys and values, print True if the smaller key is equal to the greatest value.

Example 1 - given:

d = {
    14:1,
    11:7,
     7:3,
    70:5
}

after your code, it must print True (because the smallest key is 7 which is equal to the greates value 7):

True

Example 2 - given:

d = {
    12:1,
    11:9,
     7:3,
     2:5,
     9:1
}

after your code, it must print False (because the smallest key 2 is different from the greatest value9):

False
Show solution
[22]:
d = {
    14:1,
    11:7,
     7:3,
    70:5
}

"""
d = {
    12:1,
    11:9,
     7:3,
     2:5,
     9:1
}
"""

# write here


[22]:
True

items method

We can extract all the key/value associations as a list of couples of type tuple with the method .items(). Let’s see an example which associates attractions to the city they are in:

[23]:
holiday = {'Piazza S.Marco'  :'Venezia',
           'Fontana di Trevi':'Roma',
           'Uffizi'          :'Firenze',
           'Colosseo'        :'Roma',
}
[24]:
holiday.items()
[24]:
dict_items([('Colosseo', 'Roma'), ('Fontana di Trevi', 'Roma'), ('Uffizi', 'Firenze'), ('Piazza S.Marco', 'Venezia')])

In this case we see that an object of type dict_items is returned. As in previous cases, it is a view which we can’t directly modify. If the original dictionary gets changed, the mutation will be reflected in the view:

[25]:
attractions = holiday.items()
[26]:
holiday['Palazzo Ducale'] = 'Venezia'
[27]:
attractions
[27]:
dict_items([('Colosseo', 'Roma'), ('Palazzo Ducale', 'Venezia'), ('Fontana di Trevi', 'Roma'), ('Uffizi', 'Firenze'), ('Piazza S.Marco', 'Venezia')])

QUESTION: Look at the following code fragments, and for each try guessing which result it produces (or if it gives an error):

  1. {'a':7, 'b':9}.items()[0] = ('c',8)
    
  2. dict({'a':7,'b':5}.items())['a']
    
  3. len(set({'a':'b', 'a':'B'}.items()))
    
  4. {'a':2}.items().find(('a',2))
    
  5. {'a':2}.items().index(('a',2))
    
  6. list({'a':2}.items()).index(('a',2))
    
  7. diz1 = {'a':7,
            'b':5}
    diz2 = dict(diz1.items())
    diz1['a'] = 6
    print(diz1 == diz2)
    
  8. ('a','b') in {'a':('a','b'), 'b':('a','b')}.items()
    
  9. ('a','b') in list({'a':('a','b'), 'b':('a','b')}.items())[0]
    

Exercise - union without update

Given the dictionaries d1 and d2, write some code which creates a NEW dictionary d3 containing all the key/value couples from d1 and d2.

  • we suppose all the key/value couples are distinct

  • DO NOT use cycles

  • DO NOT use .update()

  • your code must work for any d1 and d2

Example - given:

d1 = {'a':4,
      'b':7}
d2 = {'c':5,
      'd':8,
      'e':2}

after your code, it must result (order is not important):

>>> print(d3)
{'a': 4, 'e': 2, 'd': 8, 'c': 5, 'b': 7}
Show solution
[28]:
d1 = {'a':4,
      'b':7}
d2 = {'c':5,
      'd':8,
      'e':2}

# write here


update method

Having a dictionary to start with, it is possibly to MODIFY it by joining another with the method .update():

[29]:
d1 = {'goats'    :6,
      'cabbage'  :9,
      'shepherds':1}


d2 = {'goats'  :12,
      'cabbage':15,
      'benches':3,
      'hay'    :7}
[30]:
d1.update(d2)
[31]:
d1
[31]:
{'benches': 3, 'cabbage': 15, 'goats': 12, 'hay': 7, 'shepherds': 1}

Note how the common keys among the two dictionaries like 'goats' and 'cabbage' have values from the second.

If we will, it’s also possible to pass a sequence of couples like this:

[32]:
d1.update([('hay',3),('benches',18), ('barns',4)])
[33]:
d1
[33]:
{'barns': 4,
 'benches': 18,
 'cabbage': 15,
 'goats': 12,
 'hay': 3,
 'shepherds': 1}

Exercise - axby

Given a dictionary dcc which associates characters to characters and a string s formatted with couples of characters like ax separated by a semi-colon ;, substitute all the values in dcc with the corresponding values denoted in the string.

  • your code must work for any dictionary my_dict and lists

Example - given:

dcc = {
    'a':'x',
    'b':'y',
    'c':'z',
    'd':'w'
}
s = 'bx;cw;ex'

after your code, it must result:

>>> dcc
{'a': 'x', 'b': 'x', 'c': 'w', 'd': 'w', 'e': 'x'}
Show solution
[34]:
dcc = {
    'a':'x',
    'b':'y',
    'c':'z',
    'd':'w'
}
s = 'bx;cw;ex'

# write here


[34]:
{'a': 'x', 'b': 'x', 'c': 'w', 'd': 'w', 'e': 'x'}

Classes - OrderedDict

As we said before, when we print a dictionary with print or we leave the visualization to Jupyter, most of the times couples are not in insertion order. For the order to be predictable, you must use an OrderedDict

First you need to import it from the collections module:

[35]:
from collections import OrderedDict
[36]:
od = OrderedDict()

An OrderedDict appears and behaves like regular dictionaries:

[37]:
od['some key'] = 5
od['some other key'] = 7
od[('an', 'immutable','tuple', 'as key')] = 3
od['Another key'] = 'now a string!'
od[123] = 'hello'

When visualizing with Jupyter, we see the insertion order:

[38]:
od
[38]:
OrderedDict([('some key', 5),
             ('some other key', 7),
             (('an', 'immutable', 'tuple', 'as key'), 3),
             ('Another key', 'now a string!'),
             (123, 'hello')])

As we see it with a regular print:

[39]:
print(od)
OrderedDict([('some key', 5), ('some other key', 7), (('an', 'immutable', 'tuple', 'as key'), 3), ('Another key', 'now a string!'), (123, 'hello')])

Let’s see how it appears in Python Tutor:

[40]:
from collections import OrderedDict
od = OrderedDict()
od['some key'] = 5
od['some other key'] = 7
od[('an', 'immutable','tuple', 'as key')] = 3
od['Another key'] = 'now a string!'
od[123] = 'hello'

jupman.pytut()
[40]:

Exercise - phonebook

Write some code which given three tuples with names and phone numbers, PRINTS an OrderedDict which associates names to phone numbers, in the order in which are proposed

  • Your code must work with any tuple

  • Do not forget to import OrderedDict from collections

Example:

t1 = ('Alice', '143242903')
t2 = ('Bob', '417483437')
t3 = ('Carlo', '423413213')

after your code, it should result:

OrderedDict([('Alice', '143242903'), ('Bob', '417483437'), ('Charles', '423413213')])
Show solution
[41]:
t1 = ('Alice', '143242903')
t2 = ('Bob', '417483437')
t3 = ('Charles', '423413213')

# write here


Exercise - OrderedDict copy

Given an OrderedDict od1 containing English to Italian translations, create a NEW OrderedDict called od2 which contains the same translations as input PLUS the translation 'water' : 'acqua'

  • NOTE 1: your code should work with any ordered dict as input

  • NOTE 2: od2 MUST be associated to a NEW OrderedDict !!

Example - given:

od1 = OrderedDict()
od1['dog'] = 'cane'
od1['home'] = 'casa'
od1['table'] = 'tavolo'

after your code, you should obtain:

>>> print(od1)
OrderedDict([('dog', 'cane'), ('home', 'casa'), ('table', 'tavolo')])
>>> print(od2)
OrderedDict([('dog', 'cane'), ('home', 'casa'), ('table', 'tavolo'), ('water', 'acqua')])
Show solution
[42]:
from collections import OrderedDict

od1 = OrderedDict()
od1['dog'] = 'cane'
od1['home'] = 'casa'
od1['table'] = 'tavolo'

# write here


od1= OrderedDict([('dog', 'cane'), ('home', 'casa'), ('table', 'tavolo')])
od2= OrderedDict([('dog', 'cane'), ('home', 'casa'), ('table', 'tavolo'), ('water', 'acqua')])

Classes - Counter

If we need to know how many different elements there are in a sequence (in other words, if we need to calculate a frequence histogram), the class Counter from collections module comes useful. Counter is a special type of dictionary, and first of all, we must declare to Python our intention to use it:

[43]:
from collections import Counter

Suppose we want to count how many different characters there are in this list:

[44]:
my_seq = ['t', 'e', 'm', 'p', 'e', 'r', 'a', 'm', 'e', 'n', 't']

We can initialize Counter like this:

[45]:
histogram = Counter(my_seq)

If we print it, we see that the first elements are the most frequent:

[46]:
print(histogram)
Counter({'e': 3, 'm': 2, 't': 2, 'n': 1, 'a': 1, 'p': 1, 'r': 1})

WARNING: IF WE DON’T USE print JUPYTER WILL PRINT IN ALPHABETICAL ORDER!

[47]:
histogram    # careful !
[47]:
Counter({'a': 1, 'e': 3, 'm': 2, 'n': 1, 'p': 1, 'r': 1, 't': 2})

We can obtain a list with the n most frequent items by using the method most_common, which returns a list of tuples:

[48]:
histogram.most_common(5)
[48]:
[('e', 3), ('m', 2), ('t', 2), ('n', 1), ('a', 1)]

Counter can be initialized with any sequence, for example with tuples:

[49]:
ct = Counter((50,70,40,60,40,50,40,70,50,50,50,60,50,30,50,30,40,50,60,70))
print(ct)
Counter({50: 8, 40: 4, 60: 3, 70: 3, 30: 2})

or strings:

[50]:
cs = Counter('condonation')
[51]:
print(cs)
Counter({'n': 3, 'o': 3, 'a': 1, 'c': 1, 'd': 1, 't': 1, 'i': 1})

For other methods we refer to Python documentation

Exercise - saddened

Given a string s, write some code which prints:

  • the most frequent character

  • the least frequent character

  • how many and which different frequencies there are

  • Your code must work with any string s

  • Ignore the possibility there could be ties among the most/least frequent items

  • remember to import Counter from collections

Example - given:

s = 'saddened'

your code must print:

Among the most frequent ones we find ('d', 3)
Among the least frequent ones we find ('a', 1)
There are 3 different frequencies: {1, 2, 3}
Show solution
[52]:

s = 'saddened'

# write here


Among the most frequent ones we find ('d', 3)
Among the least frequent ones we find ('s', 1)
There are 3 different frequencies: {1, 2, 3}

Continue

Go on with Dictionaries 4

[ ]: