Dictionaries
Dictionary Basics
Dictionaries are collections of key-value pairs, similar to hash maps or associative arrays in other languages.
>>> things = {"ducks": 2, "keyboards": 1}
>>> things
{'ducks': 2, 'keyboards': 1}
We can see that the type of this new object is dict (short for dictionary):
>>> type(things)
<class 'dict'>
Note that the keys can be any immutable type. So we could also use numbers or tuples as the dictionary key.
We can retrieve particular values based on their key:
>>> things["ducks"]
2
We can also add or update key-value pairs:
>>> things["stickers"] = 6
>>> things
{'ducks': 2, 'keyboards': 1, 'stickers': 6}
>>> things["stickers"] = 8
>>> things
{'ducks': 2, 'keyboards': 1, 'stickers': 8}
We can remove key-value pairs too:
>>> del things["stickers"]
>>> things
{'ducks': 2, 'keyboards': 1}
Dictionaries also have a pop method to remove items:
>>> things.pop("ducks")
2
>>> things
{'keyboards': 1}
What happens if we try to access a key that doesn’t exist?
>>> things['ducks']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'ducks'
If we aren’t sure if something is in the dictionary, and we don’t want to raise an error, we can use the get() method:
>>> things.get('ducks')
>>> print(things.get('ducks'))
None
The get() method returned None because ‘ducks’ is not in the dictionary. We can also give it a default value to use for missing keys:
>>> things.get('ducks')
>>> things.get('ducks', 0)
0
>>> things.get('keyboards', 0)
1
If we loop over a dictionary, it will only loop over the keys:
>>> things["ducks"] = 2
>>> for item in things:
... print(item)
...
keyboards
ducks
If we want to loop over key-value pairs, we can use the items method:
>>> for item, count in things.items():
... print(f"I have {count} {item}")
...
I have 1 keyboards
I have 2 ducks
Dictionary Behavior
When you check a dictionary for containment, it will check the keys, but not the values:
>>> ages = {4: 2, 3: 1}
>>> 4 in ages
True
>>> 1 in ages
False
Dictionaries are truthy if they contain any key-value pairs and falsey if they are empty:
>>> things = {}
>>> if not things:
... print("There are no things")
...
There are no things
>>> things['hat'] = 2
>>> things
{'hat': 2}
>>> if not things:
... print("There are no things")
...
The list, int, and str, functions we already learned about are actually constructors:
>>> list()
[]
>>> int()
0
>>> str()
''
Dictionaries also have a constructor called dict.
>>> dict()
{}
Dictionaries can be created from lists of two-item tuples (or lists of any two-item iterables):
>>> letters = [('a', 0), ('b', 1)]
>>> letter_dict = dict(letters)
>>> letter_dict
{'a': 0, 'b': 1}
The reverse of this is the items method, that extracts the key-value pair items of the dictionary:
>>> letter_dict.items()
dict_items([('a', 0), ('b', 1)])
Notice that a list isn’t returned from this:
>>> letter_items = letter_dict.items()
>>> type(letter_items)
<class 'dict_items'>
For performance reasons, Python 3 often returns collections that are iterables, rather than regular lists. You can iterate over these collections as usual and in general they act just like lists.
If you need an actual list, you can always pass an iterable into a list constructor:
>>> list(letter_items)
[('a', 0), ('b', 1)]
When we use the items method to get the key, value pairs from a dictionary, it is another perfect situation to use tuple unpacking again.
I did say unpacking was important and we would see it often, right?
>>> for letter, number in letter_dict.items():
... print(f"Letter: {letter}, has number: {number}")
...
Letter: a, has number: 0
Letter: b, has number: 1
Note
Dictionary keys must be hashable objects. Strings, numbers, tuples, and most immutable objects are hashable.
Why Dictionaries?
Dictionaries are particularly useful when you you have data that you’d like to be able to find based on a unique “key” that can be used to lookup each object.
Dictionaries are sort of like database tables that have a single index: the keys we use to look up each of our data objects.
Let’s say you have dictionaries of key/value pairs that represent our coworkers.
coworkers = [
(70, 'Diane Chen'),
(34, 'Trey Hunner'),
(82, 'Peter Kronfeld'),
]
If we wanted to locate our coworkers based on id, we’ll have to loop through this list of dictionaries to find them.
for id_number, name in coworkers:
if id_number == 34:
found_name = name
break
print(found_name)
If instead we make a dictionary to store these “dictionaries” in, we could use their id number as a key to look them up by:
coworkers = {
70: 'Diane Chen',
34: 'Trey Hunner',
82: 'Peter Kronfeld',
}
>>> found_name = coworkers[34]
>>> print(found_name)
Trey Hunner
Note
Dictionaries traditionally have been “unordered”, because of the process used to store the key information. This meant that you had no way of knowing what order your dictionary items would be returned to you when looping over a dictionary - although you would always get all of the items. In Python 3.6, an improvement was made to the performance of dictionaries that also reduced the memory needed to store them. The happy result of this was that dictionaries are now guaranteed to be ordered! The order is based on the order the items are added to the dictionary.
Dictionary Exercises
Hint
If you get stuck for a minute or more, try searching Google or using help.
If you’re stuck for more than a few minutes, some of these links might be helpful for some of the exercises below:
Count Words
This is the count_words exercise in dictionaries.py. Edit the dictionaries.py file in the exercises directory to implement this exercise. To test it, run python test.py count_words in your exercises directory.
Edit the function count_words so that it accepts a string and returns a dictionary noting the number of times each word occurs in the string.
Example usage:
>>> from dictionaries import count_words
>>> count_words("oh what a day what a lovely day")
{'oh': 1, 'what': 2, 'a': 2, 'day': 2, 'lovely': 1}
Note
Want to test this out manually (instead of using python test.py)?
You could could create a file called count_words_test.py with your own test code:
from dictionaries import count_words
print('Calling count_words("oh what a day what a lovely day")')
print("Expected: {'oh': 1, 'what': 2, 'a': 2, 'day': 2, 'lovely': 1}")
print(" Actual:", count_words("oh what a day what a lovely day"))
Then you can run that file to test your code:
$ python count_words_test.py
Group By
This is the group_by exercise in dictionaries.py.
Edit the dictionaries.py file in the exercises directory to implement this exercise.
To test it, run python test.py group_by in your exercises directory.
Make a function that takes an iterable and a key function and returns a dictionary of items grouped by the values returned by the given key function.
For example, let’s say we have a list of numbers, and a function that checks their remainder when we divide them by three.
>>> numbers = [1, 4, 5, 6, 8, 19, 34, 55]
>>> def mod3(n): return n % 3
...
If we call our function with this list and key function, we should get the numbers back in a dictionary of numbers tied to their remainders (0, 1, 2).
>>> from dictionaries import group_by
>>> group_by(numbers, key_func=mod3)
{1: [1, 4, 19, 34, 55], 2: [5, 8], 0: [6]}
Phonetic
This is the phonetic.py exercise in the modules directory. Create the file phonetic.py in the modules sub-directory of the exercises directory.
To test it with the test framework, run python test.py phonetic.py from your exercises directory.
Write a program phonetic.py which will spell words entered on the command line phonetically using the NATO phonetic alphabet and print them out.
To run it manually, without using the test framework, first cd to the modules sub-directory of the exercises directory before running the following commands:
$ python phonetic.py Python
Papa
Yankee
Tango
Hotel
Oscar
November
Unknown symbols (punctuation) should be ignored.
$ python phonetic.py "Yay!"
Yankee
Alfa
Yankee
If multiple words are given, a blank line should be printed between the words.
$ python phonetic.py "Python is lovely"
Papa
Yankee
Tango
Hotel
Oscar
November
India
Sierra
Lima
Oscar
Victor
Echo
Lima
Yankee
You can use this dictionary to create your program:
alphabet = {
'a': "Alfa",
'b': "Bravo",
'c': "Charlie",
'd': "Delta",
'e': "Echo",
'f': "Foxtrot",
'g': "Golf",
'h': "Hotel",
'i': "India",
'j': "Juliett",
'k': "Kilo",
'l': "Lima",
'm': "Mike",
'n': "November",
'o': "Oscar",
'p': "Papa",
'q': "Quebec",
'r': "Romeo",
's': "Sierra",
't': "Tango",
'u': "Uniform",
'v': "Victor",
'w': "Whiskey",
'x': "X-ray",
'y': "Yankee",
'z': "Zulu",
}
Sorted Dictionary
Edit the sort_dict function in dictionaries.py to take an input dictionary and return a new dictionary that is sorted by the keys.
Note that as of Python 3.6, dictionaries are “in order”. What this means is that when you iterate over a dictionary, the entries you get are in the order that they were added to the dictionary. They might not have been added to the dictionary in an ordering that is useful. Once we have a dictionary, we want to make a new dictionary that has all the same key-value pairs, but is in sorted order when it is iterated over.
Hint: Use the built-in function sorted.
To test with the automated tests, run python test.py sort_dict from the command line.
To test the function in the REPL, you can paste the function in from your text editor or import it as shown:
>>> from dictionaries import sort_dict
>>> my_dict = {1: 'one', 6: 'six', 4: 'four', 3: 'three', 2: 'two', 5: 'five'}
>>> new_dict = sort_dict(my_dict)
>>> new_dict
{1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six'}
Double-valued Dictionary
This is the dict_from_truple exercise in dictionaries.py. Edit the dictionaries.py file in the exercises directory to implement this exercise. To test it, run python test.py dict_from_truple in your exercises directory.
Edit the function dict_from_truple so that it accepts a list of three-item tuples and returns a dictionary where the keys are the first item of each tuple and the values are a two-tuple of the remaining two items of each input tuple.
Note
This exercise is different from dict_from_tuple, which we’ll see later.
Example usage:
>>> from dictionaries import dict_from_truple
>>> dict_from_truple([(1, 2, 3), (4, 5, 6), (7, 8, 9)])
{1: (2, 3), 4: (5, 6), 7: (8, 9)}
Translate
This is the translate exercise in dictionaries.py. Edit the dictionaries.py file in the exercises directory to implement this exercise. To test it, run python test.py translate in your exercises directory.
Edit the function, translate, so that it takes a string in one language and transliterates each word into another language, returning the resulting string.
Here is an (over-simplified) example translation dictionary you can use for translating from Spanish to English:
>>> words = {'esta': 'is', 'la': 'the', 'en': 'in', 'gato': 'cat', 'casa': 'house', 'el': 'the'}
Translate a sentence using your algorithm. An example of how this function should work:
>>> from dictionaries import translate
>>> translate("el gato esta en la casa")
'the cat is in the house'
Pluck
This is the pluck exercise in dictionaries.py. Edit the dictionaries.py file in the exercises directory to implement this exercise. To test it, run python test.py pluck in your exercises directory.
Edit the function pluck so that it takes in a dictionary of dictionaries, and a string that is a period-delimited “path” to the key of the nested value that should be returned.
>>> data = {'amount': 10.64, 'category': {'name': 'Music', 'group': 'Fun'}}
>>> pluck(data, 'amount')
10.64
>>> pluck(data, 'category.group')
'Fun'
>>> pluck(data, 'category.created')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'created'
The given “period-delimited path” represents levels of keys that should be queried (assume that all keys are strings).
So given this dictionary:
>>> example = {'1': {'a': {'i': 9, 'ii': 8}, 'b': 7}, '2': 6}
These two lines are equivalent:
>>> example['1']['a']['ii']
8
>>> pluck(example, '1.a.ii')
8
Your pluck function should work for arbitrarily nested dictionary-like data.
In addition, the separator character used as the “path-delimiter” should be optionally customizable:
>>> data = {'amount': 10.64, 'category': {'name': 'Music', 'group': 'Fun'}}
>>> pluck(data, 'category/name', sep='/')
'Music'
Factors
This is the get_all_factors exercise in dictionaries.py. Edit the dictionaries.py file in the exercises directory to implement this exercise. To test it, run python test.py get_all_factors in your exercises directory.
Edit the function get_all_factors so that it takes a set of numbers and returns a dictionary containing the numbers as keys and a list of all factors as values.
>>> from dictionaries import get_all_factors
>>> get_all_factors({1, 2, 3, 4})
{1: [1], 2: [1, 2], 3: [1, 3], 4: [1, 2, 4]}
>>> get_all_factors({62, 293, 314})
{314: [1, 2, 157, 314], 293: [1, 293], 62: [1, 2, 31, 62]}
Hint
You can use this function to find the factors of any number:
def get_factors(number):
factors = []
for n in range(1, number + 1):
if number % n == 0:
factors.append(n)
return factors
ASCII Strings
This is the get_ascii_codes exercise in dictionaries.py.
Create a function that accepts a list of strings and returns a dictionary containing the strings as keys and a list of corresponding ASCII character codes as values.
>>> from dictionaries import get_ascii_codes
>>> words = ["hello", "bye", "yes", "no", "python"]
>>> get_ascii_codes(words)
{'hello': [104, 101, 108, 108, 111], 'bye': [98, 121, 101], 'yes': [121, 101, 115], 'no': [110, 111], 'python': [112, 121, 116, 104, 111, 110]}
Flipped Dictionary
This is the flip_dict exercise in dictionaries.py. Edit the dictionaries.py file in the exercises directory to implement this exercise. To test it, run python test.py flip_dict in your exercises directory.
Edit the function flip_dict, that takes a dictionary as input and returns a new dictionary with the dictionary keys and values flipped.
Example usage:
>>> from dictionaries import flip_dict
>>> flip_dict({'Python': "2015-09-15", 'Java': "2015-09-14", 'C': "2015-09-13"})
{'2015-09-15': 'Python', '2015-09-14': 'Java', '2015-09-13': 'C'}
What would happen if some values were repeated in the input dictionary? Try it out.