Reputation: 553
I'm trying to get through the book: Automate the boring stuff with python. I'm at chapter 6's practice project: Table printer. (https://automatetheboringstuff.com/chapter6/) - cmmd + F "table printer"
I've managed to find the answer but I find my understanding of nested loop pretty weak. Why does the order of nesting loop matter? how does it affect the way it prints data?
This is the data:
tableData = [['apples', 'oranges', 'cherries', 'banana'],
['Alice', 'Bob', 'Carol', 'David'],
['dogs', 'cats', 'moose', 'goose']]
This is the 1st code:
for a in range(len(tableData[0])):
for b in range(len(tableData)):
print(tableData[b][a],end = "")
print()
And it prints:
apples Alice dogs
oranges Bob cats
cherries Carol moose
banana David goose
This is the 2nd code (I switched the order nesting for a and b, without changing what I want to print):
for b in range(len(tableData)):
for a in range(len(tableData[0])):
print(tableData[b][a],end = "")
print()
And it prints this:
apples orangescherries banana
Alice Bob Carol David
dogs cats moose goose
This is the 3rd code (I didn't change the order of nesting but changed what I want to print):
for a in range(len(tableData[0])):
for b in range(len(tableData)):
print(tableData[a][b],end = "")
print()
It prints this:
apples orangescherries
Alice Bob Carol
dogs cats moose
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
printTable()
File "<pyshell#11>", line 10, in printTable
print(tableData[x][j].rjust(colWidths[0]),end = "")
IndexError: list index out of range
What i'm having difficulties understanding is:
(1) for the 1st and 2nd code - why does the order of nesting affect what gets printed?
(2) for the 1st and 3rd code - why does the order of print[a][b] matters?
I'm sorry that this is a terribly long question but I'm having quite some trouble, please help me out.
Upvotes: 3
Views: 2392
Reputation: 6736
You create tables in Python as list
containing list
s.
table = [ ["row 0, column 0", "row 0, column 1", "row 0, column 2"],
["row 1, column 0", "row 1, column 1", "row 1, column 2"],
["row 2, column 0", "row 2, column 1", "row 2, column 2"],
["row 3, column 0", "row 3, column 1", "row 3, column 2"] ]
And that's how you access an item from such a "multidimensional" list:
row = table[1] # get row 1 (2nd item of the outer list)
item = row[3] # from row 1, get column 3 (4th item of the inner list)
Or in short:
item = table[1][3] # get item 1 of the outer list, and from that inner list get item 3
So this explains why the order of arguments when accessing items from "multidimensional" lists matters, the first index selects the row, the second index selects the column (if we imagine the list of lists as table as depicted above).
Now the nested loops:
for row_number in range(len(table)): # iterate over the row indices
for column_number in range(len(table[row_number])): # iterate over the column indices
# of the current row
print("Row:", row_number, "- Column:", column_number)
print(" --> Item:", table[row_number][column_number])
# <-- jump back to the column loop head here, if elements remaining
# <-- jump back to the row loop head here, if elements remaining
So the outer loop (rows) counts slower while the inner loop (columns) counts faster, as for every single iteration step of the outer loop, the inner loop performs a complete iteration over all of its elements.
However, you should not iterate over list indexes in Python, but you can directly iterate over the list items. It also becomes clearer this way:
for row in table: # iterate over the rows (items of the outer list)
for column in row: # iterate over the columns of the current row (items of inner list)
print("Row:", row_number, "- Column:", column_number)
print(" --> Item:", table[row_number][column_number])
# <-- jump back to the column loop head here, if elements remaining
# <-- jump back to the row loop head here, if elements remaining
Upvotes: 4
Reputation: 10119
I made an MSPaint illustration and analogy that might help you.
If you think of your data as living in a rectangular grid, like a city, it could help. Imagine you have a city 4 blocks wide (west to east) and 3 blocks long (north to south). You want to visit each block.
Do you travel mostly from north to south on your visits? You must start at the first block then go 3 blocks south, then move east one block and start over from the north. Do this this 4 times. This is called "column-major order".
Do you travel mostly from west to east on your visits instead? You must start at the first block then go 4 blocks east, then move south one block and start over from the west. Do this this 3 times. This is called "row-major order".
As you can see, you visit the blocks in a different order depending on if your visiting path is row-major or column-major. Follow the arrows and match their order up with the order of your data output.
In the last case, you're still visiting in row-major order, but you're trying to go only 3 blocks east and 4 blocks south. You've mixed up the dimensions of your city and you will probably end up in the ghetto (signified in the third picture by some question marks).
Edit: Picture was in the wrong order. And my words were all mixed up. I think it's fixed now.
Upvotes: 2