 Computer Science

# Grids

A grid is a list of lists. You can think of this as a two-dimensional structure. For example, if we have this grid:

``````numbers = [
[1, 2, 3],
[4, 5, 6]
]``````

Then we can visualize it like this: In some programming languages you will see these called an array. In math you might call this a matrix.

## Printing a grid

It helps to think of a grid as a list of rows. So we can print out a grid like this:

``````def print_grid(grid):
for row in grid:
for item in row:
print(item, end=' ')
print()``````

If we call it like this:

``````numbers = [
[1, 2, 3],
[4, 5, 6]
]

print_grid(numbers)``````

then we get:

``````1 2 3
4 5 6 ``````

Go back and look at this example code. We are using `print(item, end=' ')` to print each item in each row. This will print one of the numbers, followed by a space instead of a newline. This ensures all of the numbers on the same row appear on the same line: `1 2 3`. After we done with all of the numbers in a row, we call `print()`. We don’t tell it to print anything, but print always ends with a newline, unless override this with `end`, so `print()` by itself prints a newline to end the row.

So technically, the code above prints:

``````1<space>2<space>3<space><newline>
4<space>5<space>6<space><newline>``````

## Copying a grid

Let’s write a function that copies a grid:

``````def copy_grid(grid):
new_grid = []
for row in grid:
new_row = []
for item in row:
new_row.append(item)
new_grid.append(new_row)
return new_grid``````

We need to be sure to create a new grid, using the `new_grid` variable and initializing it to an empty list `[]`. Then, each time we create a new row that is also initialized with `[]`. We append each item in a row to a new row, then append each row to the new grid.

Step through every line of this in the debugger to be sure you understand what it is doing.

If we call this function with:

``````numbers = [
[1, 2, 3],
[4, 5, 6]
]
more_numbers = copy_grid(numbers)
print(more_numbers)``````

We will see:

``[[1, 2, 3], [4, 5, 6]]``

Remember, a grid is just a nested set of lists. Each nested list is a row in the grid.

## Creating an empty grid

Sometimes you want to create an empty grid, meaning a grid filled with `None` in every row and column. Here is a function that will do that:

``````def empty_grid(num_rows, num_columns):
new_grid = []
for row in range(num_rows):
new_row = []
for column in range(num_columns):
new_row.append(None)
new_grid.append(new_row)
return new_grid``````

The function takes two parameters — `num_rows` and `num_columns`. This tells us how many rows and columns we need to create in the grid.

Like when we copy a grid, above, we create an empty grid, initialized to `[]`. Each time we create a new row, we likewise initialize it to `[]`. We append `None` to the row, one for each column. We append each row to the new grid.

We can call this function:

``print(empty_grid(3, 2))``

And this will print an empty grid of 3 rows and 2 columns:

``[[None, None], [None, None], [None, None]]``

## Testing for a jagged grid

A jagged grid is one where the rows are not all the same length.

Let’s write a function that returns `True` if a grid is jagged. Think about how you might solve this problem. Out of all of the rows of a grid, there might be just one that is too long: Or there might be many rows that are too long! Or maybe there are none!

We can loop through the grid and store the length of the first row into a variable called `size`. Then for each subsequent row, we can check if the length of that row is equal to `size`. If it is not, we can return early with `True`. Otherwise, after we loop over the entire grid, we can return `False`.

Here is some code that implements this algorithm:

``````def is_jagged(grid):
size = None
for row in grid:
if size is None:
size = len(row)
elif len(row) != size:
return True
return False``````

Notice that this kind of algorithm is a lot like finding the maximum of a list of numbers. We start with `size` equal to `None`. This means that the first time we check a row, we will set `size` equal to the length of that row. For every subsequent row, we will compare the length of the new row to `size`.

We can call this function to check for a jagged grid:

``````grid_a = [
[1, 2, 3, 4],
[5, 6, 7]
]
print(is_jagged(grid_a))

grid_b = [
[1, 2],
[3, 4],
[5, 6],
[7, 8]
]
print(is_jagged(grid_b))``````

This should print `True` and then `False`.

## Parsing grids

It will be handy to load a grid from a file. Lets imagine an input file, called `grid.txt` contains this:

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

Each row is on a separate line of the file. We have a grid containing only integers. Each integer is separated by spaces. This means we can use `split()` to get each number in each row, and then append the number to the grid. We have to be sure to convert from strings to integers as we do this.

Here is some code that reads a grid from a file:

``````def load_grid(filename):
grid = []
with open(filename) as file:
for line in file:
row = []
for item in line.strip().split():
row.append(int(item))
grid.append(row)
return grid``````

We use `line.strip().split()` to first strip off the newline at the end of the line, and then split the line into a list of strings based on the space separator. We can then append each item to its row with `row.append(int(item))`, where we are sure to convert to integers first.

Let’s load `grid.txt` into a grid and then print it:

``````grid = load_grid('grid.txt')
print(grid)``````

This should print:

``[[4, 5, 6, 7], [8, 9, 10, 11], [1, 2, 3, 4]]``

## Converting a list to a grid

Sometimes you are given just a long list of data:

``['Amy', 'Anna', 'Angela', 'Anthony', 'Angelo', 'Adrian']``

and you need to convert it to a grid. let’s write a function that does this. Whoever calls our function specifies the number of rows and the number of columns. We will assume that the number of items in the list fits the dimensions of our grid perfectly.

We need to plan this out to be sure we get it right. Let’s say we want to convert the above list into a grid that has 3 rows and 2 columns. Then: This means `row 0` has items 0 and 1 from the list, `row 1` has items 2 and 3, and the `row 2` has items 4 and 5. If we are in row `i` and column `j`, then we can calculate the item we need to place here as `i * num_columns + j`. Try out this math and be sure it is right.

If we are in row `1`, column `1`, then we should store item `1 * 2 + 1 = 3` from the list.

Here is code to do this:

``````def to_grid(items, num_rows, num_columns):
grid = []
for i in range(num_rows):
row = []
for j in range(num_columns):
row.append(items[i * num_columns + j])
grid.append(row)
return grid``````

If we run this:

``````names = ['Amy', 'Anna', 'Angela', 'Anthony', 'Angelo', 'Adrian']

name_grid = to_grid(names, 3, 2)
print_grid(name_grid)``````

We get:

``````Amy Anna
Angela Anthony