BYU logo Computer Science

Grid coordinates

We can think of a grid as a two-dimensional structure that can be accessed using coordinates (row, column):

grid with 2 rows and 3 columns, shown coordinates at the top and left

The cell that is colored blue has coordinates (1, 2), meaning it is in row 1, column 2.

Using coordinates to access grid cells

Let’s write a print_grid() function that uses coordinates to access each cell:

def print_grid(grid):
    for row in range(len(grid)):
        for column in range(len(grid[row])):
            print(grid[row][column], end='\t')
        print()
  • len(grid) = number of rows in the grid
  • grid[row] = a specific row of the grid, e.g. grid[1] = row 1
  • len(grid[row]) = number of columns in a specific row of the grid
  • grid[row][column] = item in the cell that is at location (row, grid)
  • end='\t' = prints a tab character after the contents of each cell

If we call this function:

numbers = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12]
]
print_grid(numbers)

then it will print:

1	2	3	4
5	6	7	8
9	10	11	12

Traversing a grid by columns instead of rows

What happens if we change the order of the for loops in our print_grid() function?

def print_grid_by_columns(grid):
    for column in range(len(grid[0])):
        for row in range(len(grid)):
            print(grid[row][column], end='\t')
        print()

If we run this with the grid above, we get:

1	5	9
2	6	10
3	7	11
4	8	12

The order of the loops controls the direction the grid is traversed.

Averaging the values of each row

Let’s write a function that computes the average of each row of a grid. It should return the averages in a list.

def row_average(grid):
    result = []
    for row in grid:
        result.append(sum(row) / len(row))
    return result

We are using the built-in function sum(), which can sum all the elements of a list.

If we run this function:

numbers = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12]
]
print(row_average(numbers))

we get:

[2.5, 6.5, 10.5]

Computing the maximum length of a string in a column

Working on columns is a little bit more challenging. Suppose we have a grid of strings:

fruits = [
    ['apple', 'pear', 'guava'],
    ['orange', 'banana', 'peach'],
    ['melon', 'mango', 'kiwi']
]

Let’s write a function that computes the maximum length of the strings in each column, returning those maximums in a list.

def column_max_string_length(grid):
    """Returns the maximum length of the strings in each column. Assumes the grid is regular."""
    maximums = []
    for column in range(len(grid[0])):
        max_length = None
        for row in range(len(grid)):
            item_length = len(grid[row][column])
            if max_length is None or item_length > max_length:
                max_length = item_length
        maximums.append(max_length)
    return maximums

We have to loop through column to compute its maximum. To get the number of columns we use len(grid[0]). As we look at a given column, we can loop through all the rows and check the length of the item in that particular column.

If we run this code:

fruits = [
    ['apple', 'pear', 'guava'],
    ['orange', 'banana', 'peach'],
    ['melon', 'mango', 'kiwi']
]
print(column_max_string_length(fruits))

the output is:

[6, 6, 5]

Transposing a grid

Transposing a grid means flipping it diagonally:

illustration of transposing a 2x3 grid

Notice how row 0 becomes column 0, and row 1 becomes column 1.

One way to do this is to copy each row into a column, and then append those columns into a new grid. Here is a solution that does that:

def transpose(grid):
    """Returns a tranposed copy of the grid."""
    new_grid = []
    # loop through all of the columns
    for column in range(len(grid[0])):
        # create a column
        new_column = []
        # loop through a row and append its values to a column
        for row in range(len(grid)):
            new_column.append(grid[row][column])
        # append the column to the grid
        new_grid.append(new_column)
    return new_grid

If we run this:

my_grid = [
    [1, 2],
    [3, 4],
    [5, 6],
    [7, 8]
]
print_grid(my_grid)
print_grid(transpose(my_grid))

the output is:

1	3	5	7
2	4	6	8

An alternative transpose solution

A different way to think about this is to create a new grid that has the right size we need, filled with None. This new grid needs its rows to be equal to the number of columns in the original grid. Likewise, its columns should be equal to the number of rows in the original grid. We can then copy the values from each row into each column.

def make_empty_grid(rows, columns):
    new_grid = []
    for row in range(new_num_rows):
        new_row = []
        for column in range(new_num_columns):
            new_row.append(None)
        new_grid.append(new_row)
    return new_grid


def transpose2(grid):
    """Returns a transposed copy of the grid."""

    num_rows = len(grid)
    num_columns = len(grid[0])

    # make a new grid that is the right shape
    new_grid = make_empty_grid(num_columns, num_rows)

    # populate the new grid
    for row in range(num_rows):
        for column in range(num_columns):
            new_grid[column][row] = grid[row][column]

    return new_grid

Helpful built-in functions

Python has some helpful built-in functions that you may find useful:

  • sum() - computes the sum of a list of numbers
  • max() - computes the maximum of a list of numbers
  • min() - computes the minimum of a list of numbers

Here are some examples:

numbers = [1, 2, 3, 4, 5]
sum(numbers)
min(numbers)
max(numbers)