Help support the author by donating or purchasing a copy of the book (not available yet)



Chapter 11 - for loops & Dictionaries

11.1 - for loops

So far we have been using while loops to iterate over lists and strings. I purposely chose to not mention for loops yet as using while loops helps with computational thinking in the beginning. However, we have had enough practice and if you completed the final question from the last chapter you're more than ready to move on.

In this chapter we'll be looking at for loops.

for loops are used when you have a block of code which we need to execute a known number of times (kind of similar to our do-something-n-times loop pattern). When using for loops we assume we don't need to check for some condition and we need to iterate over every value in the collection or sequence.

The for loop makes use of the in operator. The in operator checks for the presence of something in some collection.

The syntax for the for loop is as follows:

for <loop_var> in <collection>:
    # Do something

I'll show you this working in an example and then walk through how it works:

names = ["Tony", "John", "Jim"]
for name in names:
    print(name)

Running this in the terminal will give:

$ py myscript.py
Tony
John
Jim

In the above example, name is the loop variable. We can pick any variable name for this and I have chosen name as it is appropriate as we are looping through names.

The loop variable is a temporary variable and its role is to store a given element for the duration of the given iteration. Let's step through our example and see how this works.

  1. We pick the first element from the list and check if it exists
  2. It does, so we assign it to a variable called name.
  3. We enter the body of the loop and do any work we need to (printing the name in this case).
  4. We go back to start of the loop and repeat steps 1 to 4.
  5. We will get to a point when there is no "next element", in which case we exit the loop.

What if we were in a case were we didn't have something to iterate over but we wanted to execute a block of code a fixed number of times?

Python provides two built-in functions for doing this. They are the range() function and the xrange() function.

The range() function takes three parameters, a start and stop and a step. The start and step are optional. The range() function builds a sequence based on these parameters.

Let's look at it in action:

for i in range(5, 10):
    print(i)

Running this would give us:

$ py myscript.py
5
6
7
8
9

As you can see we start at 5 and go up to but not including the 10. If we omit the start parameter, then it defaults to 0 and I'm sure you can guess at this point what the step parameter does from your knowledge of it in extended slicing.

The range function has build up a sequence from 5 to 9 and we iterate over that using the for loop.

We could imagine a scenario however, where we want to do something 1 million times and we use the range function to build that sequence. Building a sequence that large takes up memory. The xrange() function tackles.

xrange() does exactly the same thing as range() except it uses a technique called lazy evaluation to save on resources. We usually use xrange when building sequences like this simply for the fact we use less memory when doing so.

I'm not going to show xrange() in action because it does the same thing as range(), just its underlying implementation is different.

11.2 - Dictionaries

So far we've met lots of different types. We have also met Lists and Strings. These two types are examples of data structures. In this section we're going to look at another Python built-in data structure called the dictionary. It is of type dict.

Dictionaries are collection types but not sequences. That is, it's elements aren't ordered like they are in a list or string.

Dictionaries are sometimes referred to as maps or hashmaps.

A dictionary is a collection of key-value pairs. A dictionary implements a mapping from a key to a value.

This is illustrated below:

    KEYS                  VALUES
____________       ____________________
|          |       |                  |
|  "Tony" -|-------|-> "457-2344356"  |
|          |       |                  |
|  "Adam" -|-------|-> "359-5550983"  |
|          |       |                  |
|----------|       |------------------|

In this example, we implement a phone book using a dictionary. If we look up the key "Tony", we are led to the value "457-2344356".

The underlying implementation of a dictionary is designed in such a way that given a key, we can retrieve the associated value with great efficiency.

The Big O complexity for the lookup operation is O(1). It is constant. It doesn't matter how large the dictionary is, the time for searching for a value is always the same. Magic right? Take it as magic, I won't be going into how it does so in this book as it's a little too advanced for beginners, but you're more than welcome to look up how it's done.

We also do not know the order in which key-value pairs are stored in a Python dictionary so don't write programs that rely on it's order. If you have a dictionary that is the same each time you run your program, the order will be different each time.

The syntax for building a dictionary is as follows:

my_dictionary = {<key1> : <value1>, <key2> : <value2>, ...., <keyN> : <valueN>}

For the above phone book example, that would be done as follows:

phonebook = {"Tony": "457-2344356", "Adam" : "359-5550983"}

Note the syntax for dictionaries uses curly braces rather than square brackets.

With dictionaries, a key can be any immutable type. The values can be of any type, including other dictionaries.

The empty dictionary is: d = {}.

11.3 - Dictionary accessing

We can index access a dictionary by keys in order to retrieve values.

This is done as follows:

>>> phonebook = {"Tony": "457-2344356", "Adam" : "359-5550983"}
>>> phonebook["Tony"]
'457-2344356'

If we try to access a dictionary based on a key that doesn't exist within the dictionary, we get an error. This error is known as a KeyError in Python.

>>> phonebook = {"Tony": "457-2344356", "Adam" : "359-5550983"}
>>> phonebook["Bob"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Bob'

Remember, dictionaries are not sequenced types so we cannot access by position. We can however use integers as keys but even then they are just that; integers.

>>> my_dict = {1: "one", 3: "three", 2: "two"}
>>> my_dict[3]
'three'
>>> my_dict[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 0

If we have a key-value pair in which the value is a list, we can index the list as follows:

>>> my_dict = {"first": [2, 4, 8]}
>>> my_dict["first"][0]
2

11.4 - Adding and removing entries

We can also add new entries into an existing dictionary. When we add a new entry, we are adding a new mapping of a key-value pair into the dictionary.

The syntax for doing this is:

dictionary[<key>] = <value>

Let's look at that in action

>>> my_dict = {}
>>> my_dict["one"] = 1
>>> my_dict
{"one":1}

As we can add entries to the dictionary without a new dictionary being created every time, dictionaries are mutable types.

Remember that the keys must be a mutable type and values can be of any type. This will catch a lot of people out.

We can remove entries from the dictionary by using the del statement. This is done as follows:

>>> phonebook = {"Tony": "457-2344356", "Adam" : "359-5550983"}
>>> del phonebook["Tony"]

In the above example we are saying, delete the entry from the dictionary who's key is "Tony".

11.5 - Dictionary operators & methods

We can get the length of a dictionary the same way we do with strings and lists:

>>> phonebook = {"Tony": "457-2344356", "Adam" : "359-5550983"}
>>> len(phonebook)
2

This returns the number of key-value pairs stored in the dictionary.

We can use the in operator to test whether or not a key exists within the dictionary

phonebook = {"Tony": "457-2344356", "Adam" : "359-5550983"}
if "Tony" in phonebook:
    print(phonebook["Tony"])
else:
    print("Key doesn't exist")

The reason I introduced dictionaries in this chapter is because we use for loops to cycle through each of a dictionaries keys.

phonebook = {"Bill": "457-2344356", "Adam" : "359-5550983"}

for name in phonebook:
    print("{}'s number is {}.".format(name, phonebook[name]))

The above code would produce the following output:

Bill's number is 457-2344356.
Adam's number is 359-5550983.

We can retrieve a list of a dictionary's keys or values by using either the keys() method or values() method respectively.

>>> phonebook = {"Bill": "457-2344356", "Adam" : "359-5550983"}
>>> phonebook.keys()
dict_keys(['Bill', 'Adam'])
>>> phonebook.values()
dict_values(['457-2344356', '359-5550983'])

These aren't really lists as we can't index them but we can iterate over them using a for loop.

I want to briefly introduce another data structure. It's called the tuple. I won't go into detail on it just yet but it's basically an immutable list. We can index into it but can't change it (much like a string).

The reason I need to let you know about it here is because we can retrieve a list of tuples using the dictionary method items().

This works as follows:

>>> my_dict = {"one": 1, "two": 2}
>>> my_dict.items()
dict_items([('two', 2), ('one', 1)])

We can see the tuples have the form (e1, e2, ....., en) where e1....en are elements.

Much like a list with parenthesis instead of square brackets.

We can iterate over these too. This works as follows:

my_dict = {"one": 1, "two": 2}

for (k, v) in my_dict.items():
    print("Key: {} and Value: {}".format(k, v))

Remember, dict.items() returns a 'list' of tuples with each tuple containing two elements, the key and the value. When we say for (k, v) in my_dict.items() we are essentially saying for each tuple in the list of tuples, assign k to the element in the first position in the tuple and v to the element in the second position.

I'll illustrate this below:

  (k,    v)
   |     |
   V     V
 ("one", 1)

k is assigned to "one" and v is assigned to 1.

Running the above code gives the following output:

Key: two and Value: 2
Key: one and Value: 1

Notice how they're not in the same order as they appear when we created the dictionary.

11.6 - Sorting dictionaries

We can sort a dictionary based on the values of it's keys using the sorted method. Let's look at how this can be done:

phonebook = {"Bill": "457-2344356", "Adam" : "359-5550983"}

for (k, v) in sorted(d.items()):
    print("Key: {} and Value: {}".format(k, v))

Running this would give the following output:

Key: Adam and Value: 359-5550983
Key: Bill and Value: 457-2344356

As you can see, the dictionary has been sorted based on the keys. Since the keys are strings, they are sorted lexicographically.

We can also sort on values, however, we don't have the knowledge to do that yet. We'll come back to that in the functions chapter.

We can however reverse the order they are sorted in. The sorted() function has an optional parameter called reverse. We can set this to True and the dictionary will be sorted in reverse order based on the keys.

phonebook = {"Bill": "457-2344356", "Adam" : "359-5550983"}

for (k, v) in sorted(d.items(), reverse=True):
    print("Key: {} and Value: {}".format(k, v))

This will give us the following output:

Key: Bill and Value: 457-2344356
Key: Adam and Value: 359-5550983

11.7 - Exercises

Question 1

Write a program that reads a file. The file will contain a number of items stocked in a shop and the number of each item available. Your program should parse the file and build a dictionary from this it, then output the stock available in alphabetical order and in a specific format. You can use the following as your input text file:

Oranges 12
Apples 10
Pears 22
Milk 7
Water Bottles 33
Chocolate Bars 11
Energy Drinks 8

Your program output should look like this:

        Apples : 10
Chocolate Bars : 11
 Energy Drinks : 8
          Milk : 7
       Oranges : 12
         Pears : 22
 Water Bottles : 33

Hint: Find out the length of the longest key.

Question 2

Write a program that reads a text file. The file is a list of contact details for people. With each contact you're given the name, phone number and email address. Your dictionary should be a mapping from contact names to email and phone number for that contact. Your program should then ask for user input. The input should be a single name. If the name can be found in the contact list then output the name and contact details for that person, otherwise output No contact with that name.

The contact list file is:

Liam 345-45643454 liam@mymail.com
Noah 324-43576413 noah.doe@gmail.com
Tony 987-56543239 tony@hr.mycompany.com
Bert 654-99275234 bert.hertz@support.hertz.co.uk

If the user then enters the following names in this order:

Tony
Noah
John
Annie
Bert

Your program should give the following output:

Name: Tony
Email: tony@hr.mycompany.com
Phone: 987-56543239

Name: Noah
Email: noah.doe@gmail.com
Phone: 324-43576413

No contact with that name

No contact with that name

Name: Bert
Email: bert.hertz@support.hertz.co.uk
Phone: 654-99275234

These details should be printed one by one as the user enters the names, not in bulk as I have above.

Question 3

In the previous chapter we tried to count how many words were contained within a piece of text. In this question you are to do the same except you should output how many times each word occurred in the text.

Again, punctuation matters. This time we want to strip any punctuation surrounding a word. Your program should not be case sensitive either, Hello and hello should count as the same word. However, something like John and John's should not be counted as the same word. In other words, you are striping leading and trailing punctuation characters.

Your program should be run as follows:

$ py dict_count.py input.txt

To test that your solution is correct, use the Second Inaugural Address of Abraham Lincoln as your input text and your program should output (this is a sample from the out):

.
.
.
the : 57
satisfactory : 1
sunk : 1
second : 1
answered : 2
knew : 1
man : 1
insurgents : 1
interest : 3
paid : 1
directed : 1
god : 5
due : 1
should : 2
none : 1
by : 6
.
.
.

You need not format the output. The length of your dictionary should be 343.

Question 4

Write a program that takes two dictionaries and outputs their intersection. The intersection of two dictionaries should be a third dictionary that contains key-value pairs that are present in both of the other two dictionaries. You can hard code these two dictionaries in. They don't need to be read from anywhere.

Run your script as follows:

$ py intersection.py 

To test if your solution is correct, you can run your program with the two following dictionaries:

d1 = {k1: True, k2: True, k3: True, k4: True}
d2 = {k6: True, k2: True, k5: True, k1: True, k8: True, k4: True}

Your program should output the following dictionary:

intersection = {k2: True, k1: True, k4: True}


Help support the author by donating or purchasing a copy of the book (not available yet)



Previous Chapter - Next Chapter