To start this guide, download this zip file.
List Patterns
We are going to show you some common patterns when using lists in your programs. Recognizing these patterns will help you know how to solve a problem you are given. If a problem looks like one of these, then you can follow this pattern to write your code.
Map
The map pattern occurs when you want to take a list of values and individually map each value in the list to a new value. Here is an example:
Original Items | New Items |
---|---|
ball | ballroom |
bath | bathroom |
bed | bedroom |
family | familyroom |
food | foodroom |
car | carroom |
Notice how we can make a new list by taking each item from the original list and concatenating ‘room’ to create a new word.
The steps for this pattern are:
- create a new, empty list
- iterate through each item in the original list
- use the item in the original list to create a new item
- append the new item to the new list
- return the new list
We can see an example of this pattern in the file called make_rooms.py
:
def make_rooms(words):
rooms = []
for word in words:
room = word + 'room'
rooms.append(room)
return rooms
if __name__ == '__main__':
some_words = ['ball', 'bath', 'bed', 'family', 'food', 'car']
rooms = make_rooms(some_words)
print(f'Original words: {some_words}')
print(f'Rooms: {rooms}')
Notice how we use the original list words
to make a new list rooms
. Every
word in words
maps to a room in rooms
.
You can see another example of this pattern in smaller_numbers.py
, which takes
a list of numbers and makes numbers smaller by dividing them by two, keeping
only the integer portion. To do this, you need to be sure to use the //
operator, which does floor division, meaning it works like regular division
but returns the largest possible integer that divides into numerator by the
denominator.
def make_smaller(numbers):
smaller_numbers = []
for number in numbers:
smaller = number // 2
smaller_numbers.append(smaller)
return smaller_numbers
if __name__ == '__main__':
original = [1, 2, 3, 4, 5, 6, 7, 8]
divided_by_two = make_smaller(original)
print(original)
print(divided_by_two)
Filter
This example illustrates the filter pattern. By filter we mean that we start with a list and use its values to calculate and return a new list that has some or all of the original values. For we could take a list of numbers and return a new list that has only the odd numbers from the original list:
Original Items | New Items |
---|---|
2 | 3 |
3 | 5 |
4 | |
5 |
The steps for this pattern are:
- create a new, empty list
- iterate through each item in the original list
- append the item to the new list if it meets some criteria
- return the new list
The file only_odds.py
has code for this example:
def only_odds(numbers: list[int]) -> list[int]:
odds = []
for number in numbers:
if (number % 2) == 1:
odds.append(number)
return odds
if __name__ == '__main__':
print(only_odds([1, 2, 3, 4, 5, 6]))
We start by initializing a variable odds
to an empty list. We then iterate
through the list and check if each number is odd. We can do this by using the
mod
operator, %
, which calculates the remainder, and check if the remainder
is zero. If it is not zero, then we have an odd number, so we can append it to
the list we store in the odds
variable.
The function finishes by returning odds
, so eventually the print
function
will print [3, 5]
.
Select
This example illustrates the select pattern, which chooses a single value from a list, such as a minimum or maximum value.
The steps for this pattern are:
- initialize a variable to store the selected value; this is set to
None
- iterate through the list
- if the variable is still None or the current item is “better” (smaller or
larger) than the value of the variable
- change the variable so it now has the current item
- if the variable is still None or the current item is “better” (smaller or
larger) than the value of the variable
- return the variable
The file called find_min.py
has an example:
def find_min(numbers: list[int]) -> int:
smallest = None
for number in numbers:
if smallest is None or number < smallest:
smallest = number
return smallest
if __name__ == '__main__':
print(find_min([3, 6, 2, 8, 1, 7]))
Here we are trying to calculate the minimum value of a list of numbers. We start
by initializing smallest
to None
, because there is no smallest number yet.
Then, when we iterate over the list, we do two checks with an if
statement.
First, we check if smallest is still None
. If it is, that means we haven’t
found a smallest number yet, so the one we are currently looking at must be the
smallest. Second, we check if the current number
is smaller than smallest
.
If either of these checks succeeds, we set smallest = number
.
Here is a chart for the iterations of the loop:
Iteration | number | smallest |
---|---|---|
0 | N/A | None |
1 | 10 | 10 |
2 | 8 | 8 |
3 | 40 | 8 |
4 | 22 | 8 |
5 | 3 | 3 |
6 | 5 | 3 |
At the end, smallest = 3
.
Notice that if we ran this code:
result = find_min([])
then we would get result = None
.
Accumulate
This example demonstrates the accumulate pattern. By accumulate, we mean calculating a single value as we iterate, like taking a sum or an average.
The steps for this pattern are:
- initialize a variable to an appropriate value (for example, zero)
- iterate through the list
- use the new item to modify the variable (for example, add it to the variable or subtract it)
- return the variable
The file average.py
contains an example:
def average(numbers: list[int]) -> float:
total = 0
for number in numbers:
total = total + number
return total / len(numbers)
if __name__ == '__main__':
print(average([1, 2, 3, 4]))
In the average()
function, we initialize the total
variable to zero. Then,
each time we iterate through the numbers, we increase total by the current
number. We finally return the total divided by the length of the list.
Here is a chart showing how this code updates the number
and total
variables
each time through the loop. Iteration number 0 is where we start before the
for
loop.
Iteration | number | total |
---|---|---|
0 | N/A | 0 |
1 | 1 | 1 |
2 | 2 | 3 |
3 | 3 | 6 |
4 | 4 | 10 |
At the end of the function, it returns total / 4
, which is 2.5
.
Multiple patterns at once
Here is a problem that requires using multiple patterns: given a list of numbers, write a function that subtracts 7 and then removes any negative numbers and any numbers greater than 10.
There is starter code in all_together.py
:
def make_it_happen(numbers: list[int]) -> list[int]:
# Write code here
pass
if __name__ == '__main__':
original = [0, 7, 2, 14, 20, 32, 5, 12]
changed = make_it_happen(original)
print(changed)
How would you solve this problem? Focus on the functions you would call in
make_it_happen()
.
Here is one idea of how to do this:
def make_it_happen(numbers: list[int]) -> list[int]:
numbers = sub_7(numbers)
numbers = filter_numbers(numbers)
return numbers
We create two functions — one to do the subtraction (which should use the map pattern) and one to do the filtering.
We can implement sub_7()
first:
def sub_7(numbers: list[int]) -> list[int]:
new = []
for number in numbers:
new.append(number - 7)
return new
This follows the map pattern. Notice that we do the mapping and appending all
in one step with new.append(number - 7)
.
We can temporarily use an empty function for filter_numbers()
. Since this
function needs to return something, we can do this with:
def filter_numbers(numbers: list[int]) -> list[int]:
return numbers
That just returns the original list unchanged. Now we can run our program, which starts with:
nums = [0, 7, 2, 14, 20, 32, 5, 12]
We get the following printed out:
[-7, 0, -5, 7, 13, 25, -2, 5]
That looks good!
Now we can implement filtering:
def should_keep(number: int) -> bool:
return number >= 0 and number <= 10
def filter_numbers(numbers: list[int]) -> list[int]:
new = []
for number in numbers:
if should_keep(number):
new.append(number)
return new
Notice that we use a separate function should_keep()
to implement the decision
about whether to keep a number in the list. We can do this with
number >= 0 and number <= 10
. We phrased this problem as removing any
negative numbers and any numbers greater than 10, but for filtering we need to
turn that into a statement about which numbers to keep: keeping all numbers
greater than or equal to zero and less than or equal to 10.
Now when we run our code we get:
[0, 7, 5]
This has kept all of the numbers between and including 0 and 10.