Dictionaries

The Dictionary Data Structure

A dictionary (python type: dict) is a collection of many values that can be indexed by any object type, unlike lists that are automatically indexed by integers. Indexes for dictionaries are called keys, and a key with its associated value is called a key-value pair. Python dictionaries use curly braces notation { key1: value1, key2: value2, ...}.

📦 A list containing names of animals ['cat', 'dog', 'hen', 'fish']

Index Value
0 'cat'
1 'dog'
2 'hen'
3 'fish'

📦 A dictionary containing ages of three people {'john': 12, 'sara': 14.5, 'colin': 13}. john-12 is a key-value pair.

Key Value
'john' 12
'sara' 14.5
'colin' 13

📦 A dictionary containing details of a person {'name': 'john', 'age': 14.5, 'sex': 'F', 'citizen': True}

Key Value
'name' 'john'
'age' 14.5
'gender' 'F'
'citizen' True

📦 Some examples of defining dictionaries.

friends = {} # empty dictionary
grades = {'TE3201': 'A'}
numbers = {1: 'one', 2: 'two', 3: 'three'}
is_nice = {'jane': False, 'hakim': True, 'ravi': True}
prices = {'bread': 3.5, 'butter': 5.0, 'banana': 0.15}

Unlike lists which contain an ordered sequence of items, dictionaries are not ordered i.e., we cannot force a key-value pair in a dictionary to be at a specific position e.g., in the example above, there is no guarantee that 'bread':'3.50' pair will be at the first position of the dictionary prices after adding more key-value pairs to it.

Exercise: Get Subject Info

Complete the get_subject_info function below to return a dictionary such that the code produces the given output.

def get_subject_info(code, name, level, is_core):
  return {} # UPDATE THIS LINE
  
print(get_subject_info('TE3201', 'Software Engineering', 3, False))
print(get_subject_info('CS1101', 'Programming Basics', 1, True))

⤵️

{'code': 'TE3201', 'name': 'Software Engineering', 'level': 3, 'core?': False}
{'code': 'CS1101', 'name': 'Programming Basics', 'level': 1, 'core?': True}
def get_subject_info(code, name, level, is_core):
  return {'code': code, ...} 
  
print(get_subject_info('TE3201', 'Software Engineering', 3, False))



Working with Dictionaries

You can use keys to access values in a dictionary.

📦 Some examples of retrieving values based on the key:

numbers = {1: 'one', 2: 'two', 3: 'three'}
prices = {'bread': 3.5, 'butter': 5.0, 'banana': 0.15}
print(type(numbers))
print(numbers[1])
print(prices['butter'])



 → 


<class 'dict'>
one
5.0

📦 Examples of adding, updating, and deleting dictionary entries:

grades = {'TE3201': 'A'}
grades['CS2103'] = 'B'
print('After adding:',grades)
grades['TE3201'] = 'A+'
print('After updating:',grades)
del grades['CS2103']
print('After deleting:', grades)




 → 



After adding: {'TE3201': 'A', 'CS2103': 'B'}
After updating: {'TE3201': 'A+', 'CS2103': 'B'}
After deleting: {'TE3201': 'A+'}

Exercise: Get, Set, Delete Score

Complete the three function below such that the code produces the given output.

Given the parameterscores is a dictionary of the format {'amy': 35, 'betty': 36, 'charlie': 30} which contains scores of a bunch of players,

  • get_score(scores, player): returns the value of the key player
  • set_score(scores, player, new_score): sets the value of the key player to the new_score
  • delete_score(scores, player): deletes the value for key player
def get_score(scores, player):
  return # UPDATE HERE!
    
def set_score(scores, player, new_score):
  pass # REPLACE THIS WITH YOUR CODE!
  
def delete_score(scores, player):
  pass # REPLACE THIS WITH YOUR CODE!
  
game1_scores = {'amy': 35, 'betty': 36, 'charlie': 30}
print(get_score(game1_scores, 'betty'))
print(get_score(game1_scores, 'amy'))
set_score(game1_scores, 'charlie', 5)
set_score(game1_scores, 'dan', 0)
print(game1_scores)
delete_score(game1_scores, 'dan')
print(game1_scores)

⤵️

36
35
{'amy': 35, 'betty': 36, 'charlie': 5, 'dan': 0}
{'amy': 35, 'betty': 36, 'charlie': 5}
def get_score(scores, player):
  return scores[player]
    
def set_score(scores, player, new_score):
  scores[player] = new_score

Trying to access a value for a non-existent key raises a KeyError exception.

📦 This exampls raises an exception because the key 'santa' does not exist in the dictionary is_nice:

is_nice = {'jane': False, 'hakim': True}
print(is_nice['santa'])

 → 
Traceback (most recent call last):
  File "python", line 9, in <module>
KeyError: 'santa'

Exercise: Get Score with Error Handling

Update the get_score1(scores, player) function to handle error situations, as described below.

Given the parameterscores is a dictionary of the format {'amy': 35, 'betty': 36, 'charlie': 30} which contains scores of a bunch of players,

  • get_score1(scores, player): returns the value of the key player. Returns an error message if the player is not in scores.
def get_score1(scores, player):
  return scores[player] # UPDATE HERE!
    
game1_scores = {'amy': 35, 'betty': 36, 'charlie': 30}
print(get_score1(game1_scores, 'charlie'))
print(get_score1(game1_scores, 'brown'))

⤵️

30
brown is not in the game!
def get_score1(scores, player):
  try:
    return scores[player]
  except KeyError:
    ...

You can use keys() and values() methods to iterate through keys and values of a dictionary, respectively.

📦 The code below shows how to iterate through keys/values of a dictionary.

prices = {'bread': 3.5, 'butter': 5.0, 'banana': 0.15}
for k in prices.keys():
  print(k, '->', prices[k])

 → 
bread -> 3.5
butter -> 5.0
banana -> 0.15

prices = {'bread': 3.5, 'butter': 5.0, 'banana': 0.15}
for v in prices.values():
  print(v)

 → 
3.5
5.0
0.15

You can use the sorted() function to sort the keys/values before iterating through them.

📦 The code below shows how to iterate through keys/values of a dictionary.

prices = {'bread': 3.5, 'butter': 5.0, 'banana': 0.15}
for k in sorted(prices.keys()):
  print(k, '->', prices[k])

 → 
banana -> 0.15
bread -> 3.5
butter -> 5.0

Exercise: Print Scorecard

Update the functions in the code to behave as described below such that the code produces the output given below the code.

Given the parameterscores is a dictionary of the format {'amy': 35, 'betty': 36, 'charlie': 30} which contains scores of a bunch of players,

  • get_team_score(scores): returns the total of all scores of all players in scores.
  • print_scorecard(scores): prints scores of each player in the format given, in the alphabetical order of player names.
def get_team_score(scores):
  total = 0
  #ADD YOUR CODE HERE!
  return total
  
def print_scorecard(scores):
  #pass #REPLACE WITH YOUR CODE!

game2_scores = {'zac': 35, 'betty': 30, 'aaron': 10}
print_scorecard(game2_scores)
print('total =', get_team_score(game2_scores))

⤵️

aaron = 10
betty = 30
zac = 35
total = 75
for p in scores.keys():
  ...
  
for p in sorted(scores.keys()):
  ...

As usual, you can use in and not in to check whether a key or a value is in a dictionary.

📦 The code below shows how to check if a certain key or a value exists in a dictionary.

prices = {'bread': 3.5, 'butter': 5.0, 'banana': 0.15}
print('bread' in prices.keys())
print('sugar' not in prices.keys())

 → 

True
True

prices = {'bread': 3.5, 'butter': 5.0, 'banana': 0.15}
print(3.5 in prices.values())
print(6.0 in prices.values())

 → 

True
False

Exercise: Add Bonus

Update the functions in the code to behave as described below such that the code produces the output given below the code.

Given the parameterscores is a dictionary of the format {'amy': 35, 'betty': 36, 'charlie': 30} which contains scores of a bunch of players,

  • add_bonus(scores, player, bonus): adds the bonus to the value of the player if player already exists in scores. Otherwise, adds the player to scores with the value bonus.
def add_bonus(scores, player, bonus):
  pass #REPLACE WITH YOUR CODE!


game3_scores = {'amy': 35, 'betty': 36, 'charlie': 30}    
add_bonus(game3_scores, 'amy', 5)
add_bonus(game3_scores, 'dan', 10)
print(game3_scores)

⤵️

{'amy': 40, 'betty': 36, 'charlie': 30, 'dan': 10}
def add_bonus(scores, player, bonus):
  if player in scores.keys():
    ...
  else:
    ...



Nested Dictionaries

Dictionaries and lists can be nested in each other to create more complicated data structures.

📦 This dictionary keeps track of assignments of each subject. Note how the value is a list rather than a simple value such as int or string.

assignments = {'TE3201': ['do exercises', 'submit project'],
               'CS2103': [],
               'CS3281': ['do presentation']}

💡 Python ignores line breaks and indentations in the middle of a dictionary/list definition. The intention is to allow you to structure the code into a more readable format (e.g., one key-value pair per line).

As the values in this dictionary are lists, you can do list operations on them, e.g., append(), len()

print('CS3281 assignments:', assignments['CS3281'])

assignments['CS3281'].append('study for exams')
print(assignments['CS3281'])

print('How many things to do in TE3201?', len(assignments['TE3201']))

# calculate total assignments
total = 0
for a in assignments.values():
  total = total + len(a)
print('total tasks to do:', total)

⤵️

CS3281 assignments: ['do presentation']
['do presentation', 'study for exams']
How many things to do in TE3201? 2
total tasks to do: 4

Exercise: Inning-Scores

Assume the game has three innings and a player can play in 0-3 innings. For each inning, a player will have a inning-score which is an integer e.g., player amy scored 5, 5, and 10 for the three innings (in order).

Update the get_detailed_scores functions in the code below such that it returns a dictionary that stores inning scores of each player as a list of integers for each player. Inning-scores of each player is given below.

Player Scores in different innings
amy 5, 5, 10
betty 10
charile (did not play any innings)
def get_detailed_scores():
  return {'amy': 20, 'betty': 30, 'charlie': 30} #REPLACE WITH YOUR CODE! 

print(get_detailed_scores())

⤵️

{'amy': [5, 5, 10], 'betty': [10], 'charlie': []}
def get_detailed_scores():
  return {'amy': [5, 5, 10],
          ...}

📦 This dictionary uses dictionaries as values (i.e., nesting dictionaries inside dictionaries). It stores details of a group of persons.

friends = {'john': {'name': 'John Doe', 'birthday': 'Jan 12'},
           'sara': {'name': 'Sara Parker', 'birthday': 'Jun 30'},
           'betty': {'name': 'Betsy Sims', 'birthday': 'Jan 12'}}
           
print(friends['john']['birthday'])

# print friends whose birthday is Jan 12

for f in friends.keys():
  details = friends[f]
  if details['birthday'] == 'Jan 12':
    print(details['name'])

⤵️

Jan 12
John Doe
Betsy Sims

💡 As friends['john'] evaluates to a dictionary, you can use the ['birthday'] notation to find the birthday of that person.
friends['john']['birthday']
{'name': 'John Doe', 'birthday': 'Jan 12'}['birthday']
'Jan 12'

Exercise: Player Stats

Assume in each inning a player gets to bat number of times and each hit can score 0, 1, 4, or 6, which are known as hit sizes. There is a need to keep track of how many times a player achieved each hit size in an inning.

Note how the player_stats dictionary in the code below uses lists which contain dictionaries to store, for each player, how many times the player scored a certain hit size, for each inning. For example, the player amy scored hit size of 0 x 0 times; hit size of 1 x 1 times; hit size of 4 x 1 times; hit size of 6 x 0 time in the first inning (which adds up to a score of 0*0 + 1*1 + 4*1 + 6*0 = 10 for her first inning).

Update the get_player_hit_count(player_stats, player, inning, hit_size) functions in the code such that it uses the player_stats dictionary to return the hit count for the player in the inning for the given hit_size.
e.g., get_player_hit_count(player_stats, 'amy', 2, 6) should return how many times amy's hits in her second inning (i.e., inning = 2) scored 6s; the return value should be 0.

player_stats = {'amy': [{0:0, 1:1, 4:1, 6:0},  # amy's first inning stats
                        {0:2, 1:5, 4:0, 6:0},  # amy's second inning stats
                        {0:0, 1:0, 4:1, 6:1}], # amy's third inning stats
                'betty': [{0:2, 1:2, 4:2, 6:0}],
                'charlie': []}
          
def get_player_hit_count(stats, player, inning, hit_size):
  return 0 #REPLACE WITH YOUR CODE!
  
print(get_player_hit_count(player_stats, 'amy', 2, 1))
print(get_player_hit_count(player_stats, 'betty', 1, 4))

⤵️

5
2

def get_player_hit_count(player_stats, player, inning, hit_size):
  return player_stats[...][inning-1][...]

Exercise: Grade Report

Write a program that reads student grades one entry at a time and print a consolidated grade report at the end. Follow the sample output given below.

-----------------------------------------------------
     ~ Welcome to Grade Report System ~
* Enter one entry at a time in the format NAME GRADE
* NAME should be one word
* Enter an empty line to proceed to printing report
-----------------------------------------------------
Student name and grade? amy A
Student name and grade? ben B
Student name and grade? ben 
Invalid input! Entry rejected.
Student name and grade? ben C D
Invalid input! Entry rejected.
Student name and grade? ben A
Student name and grade? charlie F
Student name and grade? amy A+
Student name and grade? amy D
Student name and grade? 
============ Grade Report ===============
amy : A A+ D
ben : B A
charlie : F
  • use a dictionary of this format to store grades:
    {'amy': ['A', 'A+', 'D'],
     'ben': ['B', 'A'],
     'charlie': ['F']}
    
  • The string method split() can be useful to break the user entry into name and grade.
    As you know,'aaa bbb ccc'.split() evaluates to a list ['aaa', 'bbb', 'ccc']
  • The string method join() can be useful when printing grades.
    As you know, ' '.join(['aaa', 'bbb', 'ccc']) evaluates to 'aaa bbb ccc'