Julliard
Julliard

Reputation: 553

why does order of loop nesting matter python?

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

Answers (2)

Byte Commander
Byte Commander

Reputation: 6736

You create tables in Python as list containing lists.

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

machine yearning
machine yearning

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.

Row vs column major ordering

Upvotes: 2

Related Questions