Mixed structures#
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.
Since some of the functions you’ll need to test return a float, we can’t compare for exact numbers but only for close numbers. So we’ll need the math.isclose()
function for that. Let’s then first import math
:
import math
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"
}
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#
✪✪ Define a function extract_managers
that returns the manager names in a list
Show code cell source
def extract_managers(db):
ret = []
for d in db:
ret.append(d["name"])
return ret
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#
✪✪ Define a function extract_companies
that returns the names of departments in a list.
Show code cell source
def extract_companies(db):
ret = []
for d in db:
ret.append(d["company"]["name"])
return ret
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#
✪✪ Define a function avg_age
that returns the average age of managers
Show code cell source
def avg_age(db):
s = 0
for d in db:
s += d["age"]
return s / len(db)
assert math.isclose(avg_age(managers_db), (34 + 25 + 20 + 37 + 25) / 5)
Exercise - sectors#
✪✪ Define a function sectors
that returns the company sectors in a list, WITHOUT duplicates and alphabetically sorted!!!
Show code cell source
def sectors(db):
ret = []
for d in db:
sector = d["company"]["sector"]
if sector not in ret:
ret.append(sector)
ret.sort()
return ret
assert sectors([]) == []
assert sectors(managers_db) == ["Fashion", "Food", "Precious stones"]
Exercise - averages#
✪✪ Define a function averages
that, given a dictionary structured as a tree regarding the grades of a student in class V and VI, returns an array containing the average for each subject
Show code cell source
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
Example result:
data = [
{"id": 1, "subject": "math", "V": 70, "VI": 82},
{"id": 1, "subject": "italian", "V": 73, "VI": 74},
{"id": 1, "subject": "german", "V": 75, "VI": 86},
]
averages(data)
[76.0, 73.5, 80.5]
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(data), [76.0, 73.5, 80.5])
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 returns True
if that client has the given preference, False
otherwise
Show code cell source
def has_pref(d, name, pref):
if name in d:
return pref in d[name]
else:
return False
Example result:
data = {
"aldo": ["cinema", "music", "sport"],
"giovanni": ["music"],
"giacomo": ["cinema", "videogames"],
}
# Will returns `True`, because also likes music:
print(has_pref(data, "aldo", "music"))
# Will return `False`, because giacomo doesn't like sport:
print(has_pref(data, "giacomo", "sport"))
True
False
assert not has_pref({}, "a", "x")
assert not has_pref({"a": []}, "a", "x")
assert has_pref({"a": ["x"]}, "a", "x")
assert not has_pref({"a": ["x"]}, "b", "x")
assert has_pref({"a": ["x", "y"]}, "a", "y")
assert has_pref({"a": ["x", "y"], "b": ["y", "x", "z"]}, "b", "y")
assert has_pref(data, "aldo", "music")
assert not has_pref(data, "giacomo", "sport")
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), like in the two examples given below:
shops1 = [
{"name": "La Patisserie", "cornetti": 2},
{"cornetti": 5, "name": "La Casa Del Cioccolato"},
]
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"},
]
Given a list of such dictionaries and a list of pastries pastries
, we want to produce as output a NEW list of lists, which has the totals of each pastry type, as in the example below:
Show code cell source
def festival(shops, pastries):
ret = []
# we make a copy of pastries to prevent modification of the input
ret.append(["Name"] + pastries[:])
sums = [0] * (len(pastries) + 1)
sums[0] = "Totals"
for p in shops:
j = 1
row = [p["name"]]
for pastry in pastries:
row.append(p[pastry])
sums[j] += p[pastry]
j += 1
ret.append(row)
ret.append(sums)
return ret
festival(shops2, ["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]]
pastries1 = ["cornetti"]
res1 = festival(shops1, pastries1)
assert res1 == [
["Name", "cornetti"],
["La Patisserie", 2],
["La Casa Del Cioccolato", 5],
["Totals", 7],
]
assert pastries1 == ["cornetti"] # verify the input didn't change
res2 = festival(shops2, ["bignè", "zippole", "babbà"])
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 these two are known:
title
andyear
.
For example, with the following data, you should obtain the result shown below:
films1 = [
{"title": "Pretty Woman", "year": 1990, "Edward": "Vivian"},
{"title": "Titanic", "year": 1997, "Jack": "Rose"},
]
films2 = [
{
"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,
},
]
Show code cell source
def actorswap(movies):
ret = []
for diz in movies:
nuovo = {}
ret.append(nuovo)
for k in diz:
if k == 'title' or k == 'year':
nuovo[k] = diz[k]
else:
nuovo[diz[k]] = k
return ret
actorswap(films2)
[{'title': 'Jerry Maguire', 'year': 1996, 'Dorothy': 'Jerry'},
{'title': 'Superman', 'Lois': 'Kent', 'year': 1978},
{'title': 'The Lord of the Rings', 'year': 2001, 'Arwen': 'Aragorn'},
{'Hermione': 'Ron Weasley',
'title': 'Harry Potter and the Deathly Hallows, Part 2',
'year': 2011}]
assert actorswap([]) == []
orig_film = films1[0]
res2 = actorswap(films1)
assert res2 == [
{"title": "Pretty Woman", "year": 1990, "Vivian": "Edward"},
{"title": "Titanic", "year": 1997, "Rose": "Jack"},
]
assert id(films1) != id(res2) # must produce a NEW list
assert id(orig_film) != id(res2[0]) # must produce a NEW dictionary
assert actorswap(films2) == [
{"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",
},
]