We’ve seen that a list can store sequential data, but a lot of data is more naturally represented as 2 (or more!) dimensions. For example, many games are played on rectangular grids, such as chess, checkers, and tic tac toe. The data in a spreadsheet is 2-dimensional. Images can be represented as 2-dimensional arrays of pixels.
To work with these types of data, let's think about how we can represent 2-dimensional data. Since a list can actually store any type of data, it could store… lists! If we have a list of lists, we essentially have a table or 2-dimensional “data structure.” In this topic, we will focus on 2-dimensional lists, but the concepts generalize to 3-dimensional lists and beyond.
When working with 2-dimensional data, we often want to "walk" the data by visiting every cell. It turns out that this is easily done using nested for loops. We will have an outer for loop that walks over the rows. Within that for loop, we will have another for loop that walks over every column within that row, giving us access to each cell. Examples will make this clearer!
def multTable( m:int, n:int ) -> list:
"""Create and return a multiplication table with
m+1 rows and n+1 columns. The entry at cell [r,c] is
the value r*c. Notice this means the first row and column
will be all 0s."""
# we can think of the table as a list of lists, starting with an empty list
table:list = []
# for each value between 0 and m (inclusive), we will make a "row" as a list
for rowIndex in range( 0, m+1 ):
# create an empty list to store the row of products
row:list = []
# for each value between 0 and n (inclusive), compute and store the product
for colIndex in range( 0, n+1 ):
row.append( rowIndex*colIndex )
# add the current "row" to the table
table.append( row )
# return the computed table
return table
def printTable( table:list ) -> None:
""" Given a 2D list (a list of lists), print out the entries,
with each row on a new line. """
# for each row index of the table
for rowIndex in range(len(table)):
# make a string to accumulate our entries into
rowString:str = ""
# it's a list, so walk over each entry
for colIndex in range(len(table[rowIndex])):
rowString += str( table[rowIndex][colIndex] ) + "\t"
# print it!
print( rowString )
# test out our multiplication table
multTable23:list = multTable( 2, 3 )
# and print it!
printTable( multTable23 )
Let's build a "power" table that has 2i, in index i.
What we want memory to look like:
Code to achieve this:
# make space for a list with 5 elements, with initial values of -1
powersOf2:list = [-1]*5
# walk over the list by index values
for i in range( len(powersOf2) ):
# into index i, assign the power of 2
powersOf2[i] = 2**i
Let's build a "power" table that has rc, in row r and column c (starting labels at 0).
What we want memory to look like:
Code to achieve this:
# make space for a table with 3 rows and 4 columns
# we'll build up each row one at a time
table:list = []
# make space for each of the rows we want
for r in range( 3 ):
# make space for the list for the current row
# initialize a list with 5 elements, with initial values of -1
row:list = [-1]*4
# add it to the table
table.append( row )
# now "walk" the table to populate each cell with the correct values
# walk the first dimension of rows
for r in range( len(table) ):
# walk the second dimension of columns
for c in range( len(table[r]) ):
# into the current cell, assign the desired value
table[r][c] = r**c
How can you make a copy of a 2D list?
It will help to know:
The list instance method copy creates a "shallow" copy of a list. You can use this to copy the entries of a 1-dimensional list.
def copyTable( table:list ) -> list:
""" Make a copy of a 2D table and return it. """
# use an accumulator pattern to build up the copy row by row
copiedTable:list = [] # start with the empty list
# for each row of the given list
for row in table:
# the copy instance method makes a "shallow" copy
copiedTable.append( row.copy() )
# return the copy
return copiedTable