In this lab, you’ll practice working with lists and dictionaries. Pieces of this lab are based on problems from Introduction to Python Introducing Lists and Dictionaries, and it was adapted for this course in June 2018 by R. Jordan Crouser, and updated by Alicia M. Grubb in Spring 2019/2020.

## Repeat Warning

In order to be sucessful in computer science, an important skill that you must develop is understanding how to implement and develop a specification, and knowing when you can take liberty with the specification. You also need to distingush between implementing and developing a specification. In the course work for CSC111, we ask for some things very precisely. It is important that you read every word of lab and assignment descriptions. Misreading or ignoring directions will result in incorrect software/code.

Take a few minutes and introduce yourself to your lab partner. Answer the questions: “Smarties vs. M&Ms?”, “Rockets vs. Smarties?”, “Mars/Milky Way vs Snickers vs Twix”

Setup: We added prompts to remind you to switch driver/navigator roles, but feel free to switch more frequently or mid task.

## Lists: Overview

Note: If the navigator is not comfortable reading out loud, both partners should read over this section independently.

A list is a collection of items. In general, these items should be related in some way (although there are no restrictions on what can be stored in a python list).

### Defining a list

In Python, we define a list using square brackets. To define a list, you give the name of the list, the equals sign, and the values you want to include in your list within square brackets (separated by commas). Because lists are collections of objects, it is good practice to give them a plural name, e.g.

dogs = ['border collie', 'australian cattle dog', 'labrador retriever']

If you want your code to look a little neater, you can hit return after each comma. IDLE will indent automatically:

dogs = ['border collie',
'australian cattle dog',
'labrador retriever']

### Accessing one item in a list

Just like letters in a string, items in a list are identified by their position in the list (starting with zero). This will almost certainly trip you up at some point. Programmers even joke about how often we all make “off-by-one” errors, so don’t feel bad when you make this kind of error.

To access the first element in a list:

first_dog = dogs[0]
print(first_dog)

The number in parentheses is called the index of the item. Because lists start at zero, the index of an item is always one less than its position in the list. So to get the second item in the list, we need to use an index of 1:

second_dog = dogs[1]
print(second_dog)

### Accessing the last items in a list

You can probably see that to get the last item in this list, we would use an index of 2. This works, but it would only work because our list has exactly three items. To get the last item in a list, no matter how long the list is, you can use an index of -1:

last_dog = dogs[-1]
print(last_dog)

This syntax also works for the second to last item, the third to last, and so forth. However, as with strings, you can’t use a negative number larger than the length of the list:

nonexistant_dog = dogs[-4] # error

### Looping over lists

Looping is one of the most important concepts related to lists. You can have a list with a million items in it, and in three lines of code you can write a sentence for each of those million items. Make sure you take the time to understand this section!

We can use a loop to access each of the elements in a list one by one:

for dog in dogs:
print(dog)

Let’s break this down:

• The keyword for tells Python to get ready to use a loop.
• The variable dog (with no “s” on it) is a temporary placeholder variable. This is the variable that Python will place each item in the list into, one at a time.
• The first time through the loop, the value of dog will be ‘border collie’.
• The second time through the loop, the value of dog will be ‘australian cattle dog’.
• The third time through, dog will be ‘labrador retriever’.
• After this, there are no more items in the list, and the loop will end.

At this point you should switch driver/navigator roles.

### Common list Operations

#### Modifying elements in a list

Because lists are mutable, you can change the value of any element in a list if you know the position of that item.

dogs[0] = 'australian shepherd'
print(dogs)

#### Finding an element in a list

If you want to find out the position of an element in a list, you can use the index() method.

print(dogs.index('australian cattle dog'))

This method returns a ValueError if the requested item is not in the list, e.g.

print(dogs.index('poodle'))

#### Testing whether an item is in a list

You can test whether an item is in a list using the same in keyword that we used in for...in loops, resulting in True if the item is present and False otherwise:

print('australian cattle dog' in dogs)
print('poodle' in dogs)

#### Appending items to the end of a list

We can add an item to a list using the append() method. This method adds the new item to the end of the list.

dogs.append('poodle')
for dog in dogs:
print(dog.title() + "s are cool.")

#### Inserting items into a list

We can also insert items anywhere we want in a list using the insert() method. We specify the index into which we want to insert the item, and everything from that point on is shifted one position to the right. In other words, the index of every item after the new item is increased by one:

dogs = ['border collie',
'australian cattle dog',
dogs.insert(1, 'poodle')
print(dogs)

Note that you have to give the position of the new item first, and then the value of the new item. If you do it in the reverse order, you will get an error.

#### Removing items by position

If you know the position of an item in a list, you can remove that item using the del command (short for “delete”). To use this approach, give the command del and the name of your list, with the index of the item you want to remmove in square brackets:

# Remove the first dog from the list.
del dogs[0]
print(dogs)

#### Removing items by value

You can also remove an item from a list if you know its value. To do this, we use the remove() function. Give the name of the list, followed by the method remove with the value of the item you want to remove inside the parentheses. Python looks through your list, finds the first item with this value, and removes it:

dogs = ['border collie', 'australian cattle dog', 'labrador retriever']
# Remove australian cattle dog from the list.
dogs.remove('australian cattle dog')
print(dogs)

Note, however, that only the first item with this value is removed. If you have multiple items with the same value, you will have some items with this value left in your list.

letters = ['a', 'b', 'c', 'a', 'b', 'c']
# Remove the letter a from the list.
letters.remove('a')
print(letters)

### Other methods

Want to check out all the other methods available on lists? Read the documentation!

At this point you should switch driver/navigator roles.

## Step 1: Favorite Songs

• Create a new file called lab5p1.py in which you and your partner will work. Add the usual header, including both your names.
• Create an empty list called songs.
• Use a while loop to ask the user to enter the titles of their favorite songs (one at a time). End the loop when the user types “DONE”.
• Use the .append() method to add each song title to songs.
• After the loop ends, .sort() the list and print out the songs in a nicely-formatted way (you choose how it looks).

At this point you should switch driver/navigator roles.

## Dictionaries: Overview

Note: If the navigator is not comfortable reading out loud, both partners should read over this section independently.

Dictionaries are another way to store information that is connected in some way. Dictionaries store information in key-value pairs, so that any one piece of information in a dictionary is connected to at least one other piece of information. If we want, we can define them explicitly using key:value pairs and curly braces:

python_words = {'list': 'A collection of values that are not connected, but have an order.',
'dictionary': 'A collection of key-value pairs.',
'function': 'A named set of instructions that defines a set of actions in Python.',
}

To add a new key-value pair, you give the dictionary name followed by the new key in square brackets, and set that equal to the new value. We will show this by starting with an empty dictionary, and re-creating the dictionary from the example above.

# Create an empty dictionary.
python_words = {}
# Fill the dictionary, pair by pair.
python_words['list'] ='A collection of values that are not connected, but have an order.'
python_words['dictionary'] = 'A collection of key-value pairs.'
python_words['function'] = 'A named set of instructions that defines a set of actions in Python.'

Remember: dictionaries do not store their information in any particular order, so you may not get your information back in the same order you entered it.

We can get individual items out of the dictionary by giving the dictionary’s name, followed by the key in square brackets:

print("\nWord: {0}".format('list'))
print("Meaning: {0}".format(python_words['list']))

print("\nWord: {0}".format('dictionary'))
print("Meaning: {0}".format(python_words['dictionary']))
print("\nWord: {0}".format('function'))
print("Meaning: {0}".format(python_words['function']))

This code looks pretty repetitive, and it is. Dictionaries have their own for loop syntax, but since there are two kinds of information in dictionaries, the structure is a bit more complicated than it is for lists. Here is how to use a for loop with a dictionary:

# Print out the items in the dictionary.
for word, meaning in python_words.items():
print("\nWord: {0}".format(word))
print("Meaning: {0}".format(meaning))

The output is identical, but we did it in 3 lines instead of 6. If we had 100 terms in our dictionary, we would still be able to print them out with just 3 lines.

The only tricky part about using for loops with dictionaries is figuring out what to call those first two variables. The general syntax for this for loop is:

for key_name, value_name in dictionary_name.items():
print(key_name) # The key is stored in whatever you called the first variable.
print(value_name) # The value associated with that key is stored in your second variable.

If you attempt to access an entry that is not in the dictionary, Python will return an error. For example,

python_words['noItem']

Will return the following error:

Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
KeyError: 'noItem'

To check if an entry is in the dictionary use the .get(key) function, which returns the value or None if the key is not in the dictionary. For example:

print(python_words.get('noItem'))
print(python_words.get('list'))

will have the following output:

None
A collection of values that are not connected, but have an order.

At this point you should switch driver/navigator roles.

### Modifying values in a dictionary

At some point you may want to modify one of the values in your dictionary. Modifying a value in a dictionary is similar to modifying an element in a list. You give the name of the dictionary and then the key in square brackets, and set that equal to the new value.

# Clarify one of the meanings.
python_words['dictionary'] = 'A collection of key-value pairs. Each key can be used to access its corresponding value.'
print('dictionary:', python_words['dictionary'])

### Removing key-value pairs

You may want to remove some key-value pairs from one of your dictionaries at some point. You can do this using the same del command you learned to use with lists. To remove a key-value pair, you give the del command, followed by the name of the dictionary, with the key that you want to delete. This removes the key and the value as a pair.

python_words = {'list': 'A collection of values that are not connected, but have an order.',
'dictionary': 'A collection of key-value pairs.',
'function': 'A named set of instructions that defines a set of actions in Python.',
}
# Show the current set of words and meanings.
print("\n\nThese are the Python words I know:")
for word, meaning in python_words.items():
print("\nWord: {0}".format(word))
print("Meaning: {0}".format(meaning))

# Remove the word 'list' and its meaning.
del python_words['list']
# Show the current set of words and meanings.
print("\n\nThese are the Python words I know:")
for word, meaning in python_words.items():
print("\nWord: {0}".format(word))
print("Meaning: {0}".format(meaning))

If you were going to work with this code, you would certainly want to put the code for displaying the dictionary into a function. Let’s see what this looks like:

def show_words_meanings(python_words):
# This function takes in a dictionary of python words and meanings,
#  and prints out each word with its meaning.
print("\n\nThese are the Python words I know:")
for word, meaning in python_words.items():
print("\n{0}: {1}".format(word, meaning))

python_words = {'list': 'A collection of values that are not connected, but have an order.',
'dictionary': 'A collection of key-value pairs.',
'function': 'A named set of instructions that defines a set of actions in Python.',
}
show_words_meanings(python_words)

# Remove the word 'list' and its meaning.
del python_words['list']
show_words_meanings(python_words)

This is starting to look like much more realistic code.

At this point you should switch driver/navigator roles.

## Step 2: Mountain Heights

• Wikipedia has a list of the tallest mountains in the world, complete with each mountain’s elevation. Pick your five favorite mountains from this list.
• Create a new file called lab5p2.py in which you and your partner will work. Add the usual header, including both your names.
• Create a dictionary with the mountain names as keys, and the elevations as values.
• Print out just the mountains’ names by looping through the keys of your dictionary.
• Print out just the mountains’ elevations by looping through the values of your dictionary.
• Print out a series of statements telling how tall each mountain is: “Everest is 8848 meters tall.”
• Put each of these in their own function.

At this point you should switch driver/navigator roles.

Making a YouTube playlist with lists and dictionaries.

Create a new file called lab5p3.py in which you and your partner will work. Add the usual header, including both your names.

import webbrowser
from time import sleep
def playNewSong():
title = input("Song title: ")
duration = input("Video duration (MM:SS): ")

# Convert 'duration' string into seconds
time = duration.split(":")
total_seconds = int(time[0])*60 + int(time[1])
print("Now playing", title)
sleep(2) # Wait 2 seconds before opening the video

webbrowser.open(url)

sleep(total_seconds) # Wait until the video is over
print("Song is over!")

This function asks the user to enter a song title, youtube link, and the duration of the video. It then prints the song information, launches a browser window containing the YouTube video (which should play automatically), and then goes to sleep for the duration of the song.

• Rather than storing the data in individual variables, organize them into a dictionary called song that has keys title, url, and duration
• Add a loop that allows the user to enter as many songs as they want. Store each song in a list called playlist (this will be a list of dictionaries) End the loop when the user types DONE.
• Using the code in playNewSong() (above), write a new function called playSong(title, url, duration) that plays a single song given the parameters (title, url, duration).
• Next write a function called playSongs(song_dict) that takes a songs dictionary and makes a random playlist and plays all the songs in the playlist. This function should call playSong and be called after the user enters DONE.
• Now your playlist program is complete.

## Lab Submission

If you have time, we encourage you to get started on your homework assignment in lab this week.

## (Bonus) List Comprehensions

Note: this section is included because people have been asking about it. If you’re already feeling a little shaky on lists, don’t worry. For now, it is good enough to know they exist, and to recognize them when you see them. If you like them, go ahead and start trying to use them now.

Let’s consider how we might make a list of the first ten square numbers. We could do it like this:

# Store the first ten square numbers in a list.
# Make an empty list that will hold our square numbers.
squares = []
# Go through the first ten numbers, square them, and add them to our list.
for number in range(1,11):
new_square = number**2
squares.append(new_square)

# Show that our list is correct.
for square in squares:
print(square)

This should make sense at this point. If it doesn’t, go over the code with these thoughts in mind:

• We make an empty list called squares that will hold the values we are interested in.
• Using the range() function, we start a loop that will go through the numbers 1-10.
• Each time we pass through the loop, we find the square of the current number by raising it to the second power.
• We add this new value to our list squares.
• We go through our newly-defined list and print out each value

Now let’s make this code more efficient. We don’t really need to store the new square in its own variable new_square; we can just add it directly to the list of squares:

# Store the first ten square numbers in a list.
# Make an empty list that will hold our square numbers.
squares = []
# Go through the first ten numbers, square them, and add them to our list.
for number in range(1,11):
squares.append(number**2)

# Show that our list is correct.
for square in squares:
print(square)

List comprehensions allow us to collapse the first three lines of code into one line. Here’s what it looks like:

# Store the first ten square numbers in a list.
squares = [number**2 for number in range(1,11)]
# Show that our list is correct.
for square in squares:
print(square)

It should be pretty clear that this code is more efficient than our previous approach, but it may not be clear what is happening. Let’s take a look at everything that is happening in that first line:

We first define a list called squares.

Look at the second part of what’s in square brackets:

for number in range(1,11)

This sets up a loop that goes through the numbers 1-10, storing each value in the variable number. Now we can see what happens to each number in the loop:

number**2

Each number is raised to the second power, and this is the value that is stored in the list we defined. We might read this line in the following way:

squares = [raise number to the second power, for each number in the range 1-10]`