Dictionaries

As you just saw, Python’s lists are ordered sets of objects that you access via their position/index. Dictionaries are unordered sets in which the objects are accessed via their keys. In other words, dictionaries are unordered key-value pairs.

Consider two lists:

names = ['Mary', 'John', 'Eric', 'Jeff', 'Anne']               # people
colours = ['orange', 'green', 'turquoise', 'burgundy', 'turquoise'] # and their respective favourite colours

There is nothing connecting these two lists, as far as figuring a person’s favourite colour goes. You could do something like this using indices:

colours[names.index('Eric')]

but this is a little too convoluted … A dictionary can help you connect the two datasets directly:

fav = {}  # start with an empty dictionary
for name, colour in zip(names, colours):   # go through both lists simultaneously
    fav[name] = colour

fav   # {'Mary': 'orange', 'John': 'green', 'Eric': 'turquoise', 'Jeff': 'burgundy', 'Anne': 'turquoise'}

fav['John']      # returns 'green'
fav['Mary']      # returns 'orange'
for key in fav:
    print(key, fav[key])   # will print the names (keys) and the colours (values)

You can also cycle using .keys(), .values() and .items() methods:

for k in fav.keys():
	print(k, fav[k])     # the same as above

for v in fav.values():
	print(v)              # cycle through the values

for i, j in fav.items():
	print(i,j)            # both the names and the colours
Topic 7.1

Merge two Python dictionaries

f1 = {'Mary': 'orange', 'John': 'green', 'Eric': 'turquoise'}
f2 = {'Jeff': 'burgundy', 'Anne': 'turquoise'}

into one. There are many solutions – you can google this problem. Start with:

fav = f1.copy()   # create a copy of f1
...
 

There are other ways to organize the same information using dictionaries. For example, you can create a list of dictionaries, one dictionary per person:

names = ['Mary', 'John', 'Eric', 'Jeff', 'Anne']   # people names
colours = ['orange', 'green', 'turquoise', 'burgundy', 'turquoise'] # and their respective favourite colours
ages = [25, 23, 27, 32, 26]                        # let's include a third attribute

data = []
for name, colour, age in zip(names, colours, ages):   # go through both lists simultaneously
    data.append({'name': name, 'colour': colour, 'age': age})

person = data[0]
print(person)
print(person["name"], person["colour"])

The benefit of this approach is that you can have many more attributes per person than just name and colour, and this is a very common way to organize structured and/or hierarchical data in Python. The downside is that – to search for by name – you have to do it explicitly:

for person in data:
    if person["name"]=="Jeff": print(person["colour"], person["age"])

or in a single line:

[(person["colour"], person["age"]) for person in data if person["name"]=="Jeff"]

Finally, if you want performance, you might want to consider the following approach:

for i in filter(lambda x: x%2 == 0, range(1,11)):
    print(i)

Here we:

  1. apply the lambda (anonymous) function  lambda x: x%2 == 0  to each item in range(1,11); it returns True or False,
  2. create an iterator yielding only those items in range(1,11) for which the lambda function produced True, and
  3. cycle through this iterator.

Using this approach, we can create an iterator of all people matching a name:

list(filter(lambda person: person["name"] == "Jeff", data))

Here we:

  1. apply the lambda function lambda person: person["name"] == "Jeff" to each item in the list data; it returns True or False,
  2. create an iterator yielding only those items in data for which the lambda function produced True, and
  3. create a list from this iterator, in this case containing only one element.
Topic 7.2 Write a (one-line) code to filter out all people who’s favourite colour is turquoise.
 

Going back to the basics, you can see where dictionary got its name:

concepts = {}
concepts['list'] = 'an ordered collection of values'
concepts['dictionary'] = 'a collection of key-value pairs'
concepts

Let’s modify values:

concepts['list'] = concepts['list'] + ' - very simple'
concepts['dictionary'] = concepts['dictionary'] + ' - used widely in Python'
concepts

Deleting dictionary items:

concepts.pop('list')   # remove the key 'list' and its value

Values can also be numerical:

grades = {}
grades['mary'] = 5
grades['john'] = 4.5
grades

And so can be the keys:

grades[1] = 2
grades

“Sorting” dictionary items

Let’s go back to our original dictionary:

fav = {'Mary': 'orange', 'John': 'green', 'Eric': 'turquoise', 'Jeff': 'burgundy', 'Anne': 'turquoise'}
sorted(fav)             # returns the sorted list of keys
sorted(fav.keys())      # the same
sorted(fav.values())    # returns the sorted list of values
for k in sorted(fav):
	print(k, fav[k])    # full dictionary sorted by key
Topic 7.3

Write a script to print the full dictionary (keys and values) sorted by the value.

Hint: create a list comprehension looping through all (key,value) pairs and then try sorting the result.

 

Dictionary comprehensions

Similar to list comprehensions, we can form a dictionary comprehension:

{k:'.'*k for k in range(10)}
{k:v*2 for (k,v) in zip(range(10),range(10))}
{j:c for j,c in enumerate('computer')}
Topic 7.4

Suppose we have a programmatically-generated list of random emails addresses with SFU, UBC, BCIT, and gmail domains:

import random
domains = ['sfu.ca', 'ubc.ca', 'bcit.ca', 'gmail.com']
random.choice(domains)
random.seed(10)
emails = [str(random.randint(1,1000)).zfill(5) + '@' + random.choice(domains) for i in range(1000)]

Write a Python code to count the number of addresses from each domain and report the answer as a dictionary:

{'sfu': 251, 'ubc': 235, 'bcit': 232, 'gmail': 282}

Try doing it without using Counter library.

Hint: For each domain, try creating a list of True/False values telling whether that domain is present in each element.Can you solve the entire problem in one line?