Mixed structures 1

Download exercises zip

Naviga file online

In this notebook we will see how to manage more complex data structures like lists of dictionaries and dictionaries of lists, examining also the meaning of shallow and deep copy.

WARNING

The following exercises contain tests with asserts. To understand how to carry them out, read first Error handling and testing

Exercise - Luxury Holding

A luxury holding groups several companies and has a database of managers as a list of dictionaries. Each employee is represented by a dictionary:

{
    "name":"Alessandro",
    "surname": "Borgoloso",
    "age": 34,
    "company": {
                   "name": "Candied Herrings",
                   "sector":"Food"
               }
},

The dictionary has several simple attributes like name, surname, age. The attribute company is more complex, because it is represented as another dictionary:

"company": {
               "name": "Candied Herrings",
               "sector":"Food"
           }
[1]:
managers_db = [
  {
    "name":    "Alessandro",
    "surname": "Borgoloso",
    "age":     34,
    "company": {
                   "name": "Candied Herrings",
                   "sector":"Food"
               }
  },
  {
    "name":    "Matilda",
    "surname": "Delle Sòle",
    "age":     25,
    "company": {
                    "name": "Pythonic Footwear",
                    "sector":"Fashion"
               }
  },
  {
    "name":    "Alfred",
    "surname": "Pennyworth",
    "age":     20,
    "company": {
                    "name": "Batworks",
                    "sector":"Fashion"
               }
  },
  {
    "name":    "Arianna",
    "surname": "Schei",
    "age":     37,
    "company": {
                    "name": "MegaDiamonds Unlimited",
                    "sector":"Precious stones"
               }
  },
  {
    "name":    "Antonione",
    "surname": "Cannavacci",
    "age":     25,
    "company": {
                    "name": "Pre-chewed Chewing gums",
                    "sector":"Food"
               }
  },
]

Exercise - extract_managers

✪✪ RETURN the manager names in a list

Show solution
[2]:

def extract_managers(db):
    raise Exception('TODO IMPLEMENT ME !')

assert extract_managers([]) == []

# if it doesn't find managers_db, remember to execute the cell above which defines it !
assert extract_managers(managers_db) == ['Alessandro', 'Matilda', 'Alfred', 'Arianna', 'Antonione']

Exercise - extract_companies

✪✪ RETURN the names of departments in a list.

Show solution
[3]:
def extract_companies(db):
    raise Exception('TODO IMPLEMENT ME !')


assert extract_companies([]) == []
# if it doesn't find managers_db, remember to execute the cell above which defines it !
assert extract_companies(managers_db) == ["Candied Herrings","Pythonic Footwear","Batworks","MegaDiamonds Unlimited","Pre-chewed Chewing gums"]

Exercise - avg_age

✪✪ RETURN the average age of managers

Show solution
[4]:

def avg_age(db):
    raise Exception('TODO IMPLEMENT ME !')


# since the function returns a float we can't compare for exact numbers but
# only for close numbers with the function math.isclose
import math
assert math.isclose(avg_age(managers_db), (34 + 25 + 20 + 37 + 25) / 5)

Exercise - sectors

✪✪ RETURN the company sectors in a list, WITHOUT duplicates and alphabetically sorted!!!

Show solution
[5]:
def sectors(db):
    raise Exception('TODO IMPLEMENT ME !')


assert sectors([]) == []
assert sectors(managers_db) == ["Fashion", "Food", "Precious stones"]

Exercise - averages

✪✪ Given a dictionary structured as a tree regarding the grades of a student in class V and VI, RETURN an array containing the average for each subject

Example:

>>> averages([
      {'id' : 1, 'subject' : 'math',    'V' : 70, 'VI' : 82},
      {'id' : 1, 'subject' : 'italian', 'V' : 73, 'VI' : 74},
      {'id' : 1, 'subject' : 'german',  'V' : 75, 'VI' : 86}
    ])

returns

[ 76.0 , 73.5, 80.5 ]

which corresponds to

[ (70+82)/2 , (73+74)/2, (75+86)/2 ]
[6]:
def averages(lst):
    ret = [0.0, 0.0, 0.0]

    for i in range(len(lst)):
        ret[i] = (lst[i]['V'] + lst[i]['VI']) / 2

    return ret


# TEST START - DO NOT TOUCH!
# if you wrote the whole code correct, and execute the cell, Python shouldn't raise `AssertionError`
import math

def is_list_close(lista, listb):
    """ Verifies the float numbers in lista are similar to numbers in listb

    """

    if len(lista) != len(listb):
        return False

    for i in range(len(lista)):
        if not math.isclose(lista[i], listb[i]):
            return False

    return True

assert is_list_close(averages([
                            {'id' : 1, 'subject' : 'math', 'V' : 70, 'VI' : 82},
                            {'id' : 1, 'subject' : 'italian', 'V' : 73, 'VI' : 74},
                            {'id' : 1, 'subject' : 'german', 'V' : 75, 'VI' : 86}
                          ]),
                     [ 76.0 , 73.5, 80.5 ])
# TEST END

Exercise - has_pref

✪✪ A big store has a database of clients modelled as a dictionary which associates customer names to their preferences regarding the categories of articles the usually buy:

{
    'aldo':     ['cinema', 'music', 'sport'],
    'giovanni': ['music'],
    'giacomo':  ['cinema', 'videogames']
}

Given the dictionary, the customer name and a category, write a function has_pref which RETURN True if that client has the given preference, False otherwise

Example:

ha_pref({
            'aldo':     ['cinema', 'musica', 'sport'],
            'giovanni': ['musica'],
            'giacomo':  ['cinema', 'videogiochi']

        }, 'aldo', 'musica')

must return True, because aldo likes music, while instead:

has_pref({'aldo':    ['cinema', 'music', 'sport'],
         'giovanni': ['music'],
         'giacomo':  ['cinema', 'videogames']

        }, 'giacomo', 'sport')

must return False because giacomo doesn’t like sport.

Show solution
[7]:

def has_pref(d, name, pref):
    raise Exception('TODO IMPLEMENT ME !')

assert has_pref({}, 'a', 'x') == False
assert has_pref({'a':[]}, 'a',  'x') == False
assert has_pref({'a':['x']}, 'a',  'x') == True
assert has_pref({'a':['x']}, 'b',  'x') == False
assert has_pref({'a':['x','y']}, 'a',  'y') == True
assert has_pref({'a':['x','y'],
                   'b':['y','x','z']}, 'b',  'y') == True
assert has_pref({'aldo':     ['cinema', 'music', 'sport'],
                 'giovanni': ['music'],
                 'giacomo':  ['cinema', 'videogames']
                }, 'aldo', 'music') == True
assert has_pref({'aldo':     ['cinema', 'music', 'sport'],
                 'giovanni': ['music'],
                 'giacomo':  ['cinema', 'videogames']
                }, 'giacomo', 'sport') == False

Exercise - festival

✪✪✪ During a country festival in Italy, the local pastry shops decide to donate each a certain amount of pastries. Every shop is represented as a dictionary, which contains pastries names as keys, plus the special key name which represents the shop name itself (assume all the shops produce the same types of pastries)

shops = [{'babbà':  3,
          'bignè':  4,
          'zippole':2,
          'name':  'Da Gigi'},
         {'babbà':  5,
          'bignè':  3,
          'zippole':9,
          'name':   'La Delizia'},
         {'babbà':  1,
          'bignè':  2,
          'zippole':6,
          'name':   'Gnam gnam'},
         {'babbà':  7,
          'bignè':  8,
          'zippole':4,
          'name':   'Il Dessert'}]

Given a list of such dictionaries and a list of pastries pastries, we want to produce as output a NEW list of lists structured like this:

>>> festival(pastries, ['bignè', 'zippole', 'babbà'])

[['Name', 'bignè', 'zippole', 'babbà'],
 ['Da Gigi', 4, 2, 3],
 ['La Delizia', 3, 9, 5],
 ['Gnam gnam', 2, 6, 1],
 ['Il Dessert', 8, 4, 7],
 ['Totals', 17, 21, 16]]

which has the totals of each pastry type.

Show solution
[8]:

def festival(shops, pastries):
    raise Exception('TODO IMPLEMENT ME !')

from pprint import pprint

pastries1 = ['cornetti']
res1 = festival([{'name':'La Patisserie',
                  'cornetti':2},
                 {'cornetti':5,
                  'name':'La Casa Del Cioccolato'}],
                pastries1)
assert res1 == [['Name',                  'cornetti' ],
                ['La Patisserie',              2     ],
                ['La Casa Del Cioccolato',     5     ],
                ['Totals',                     7     ]]
assert pastries1 == ['cornetti'] # verify the input didn't change

shops2 = [{'babbà':  3,
           'bignè':  4,
           'zippole':2,
           'name':   'Da Gigi'},
          {'babbà':  5,
           'bignè':  3,
           'zippole':9,
           'name':   'La Delizia'},
          {'babbà':  1,
           'bignè':  2,
           'zippole':6,
           'name':   'Gnam gnam'},
          {'babbà':  7,
           'bignè':  8,
           'zippole':4,
           'name':   'Il Dessert'}]


res2 = festival(shops2, ['bignè', 'zippole', 'babbà'])
#pprint(res2, width=43)

assert res2 == [ ['Name', 'bignè', 'zippole', 'babbà'],
                 ['Da Gigi', 4, 2, 3],
                 ['La Delizia', 3, 9, 5],
                 ['Gnam gnam', 2, 6, 1],
                 ['Il Dessert', 8, 4, 7],
                 ['Totals', 17, 21, 16] ]

Exercise - actorswap

✪✪✪ Given a movie list movies where each movie is represented as a dictionary, RETURN a NEW list with NEW dictionaries having the male actor names swapped with the female ones.

  • ONLY swap actor names

  • you can’t predict actor names

  • you only know each dictionary holds exactly three keys, of which two are known: title and year.

Example:

db = [
            {'title': 'Jerry Maguire',
             'year': 1996,
             'Jerry': 'Dorothy',},
            {'title': 'Superman',
             'Kent': 'Lois',
             'year': 1978},
            {'title': 'The Lord of the Rings',
             'year': 2001,
             'Aragorn': 'Arwen',},
            {'Ron Weasley': 'Hermione',
             'title': 'Harry Potter and the Deathly Hallows, Part 2',
             'year': 2011}
]

>>> actorswap(db)
[{'title': 'Jerry Maguire',
  'year': 1996,
  'Dorothy': 'Jerry'},
 {'title': 'Superman',
 'year': 1978,
 'Lois': 'Kent'},
 {'title': 'The Lord of the Rings',
 'year': 2001,
 'Arwen': 'Aragorn'},
 {'title': 'Harry Potter and the Deathly Hallows, Part 2',
 'year': 2011,
 'Hermione': 'Ron Weasley',
 }]
Show solution
[9]:

def actorswap(movies):
    raise Exception('TODO IMPLEMENT ME !')

# TEST START
l1 = []
assert actorswap(l1) == []

l2 = [ {'title': 'Pretty Woman',
        'year': 1990,
        'Edward':'Vivian'},
       {'title': 'Titanic',
        'year': 1997,
        'Jack' : 'Rose'}
     ]
orig_film = l2[0]
res2 = actorswap(l2)
assert res2 == [ {'title': 'Pretty Woman',
                   'year': 1990,
                   'Vivian':'Edward'},
                 {'title': 'Titanic',
                  'year': 1997,
                  'Rose' : 'Jack'}
               ]
assert id(l2) != id(res2)            # must produce a NEW list
assert id(orig_film) != id(res2[0])  # must produce a NEW dictionary

l3 = [
        {'title':'Jerry Maguire',
         'year':1996,
         'Jerry':'Dorothy',},
        {'title':'Superman',
         'Kent':'Lois',
         'year': 1978},
        {'title':'The Lord of the Rings',
         'year': 2001,
         'Aragorn':'Arwen',},
        {'Ron Weasley':'Hermione',
         'title': 'Harry Potter and the Deathly Hallows, Part 2',
         'year': 2011}
]

assert actorswap(l3) == [{'title': 'Jerry Maguire',
                          'year': 1996,
                          'Dorothy': 'Jerry'},
                         {'title': 'Superman',
                          'year': 1978,
                          'Lois': 'Kent'},
                         {'title': 'The Lord of the Rings',
                          'year': 2001,
                          'Arwen': 'Aragorn'},
                         {'title': 'Harry Potter and the Deathly Hallows, Part 2',
                          'year': 2011,
                          'Hermione': 'Ron Weasley',
                         }]

Continue

Go on with the challenges

[ ]: