COMPUTER PROGRAMMING 1 (PYTHON) • CORE DATA STRUCTURES

2D Lists — I can represent 2D data as a list of lists and process it with nested loops at my level.

Learn to organize grid-like data in Python using nested lists and traverse every element with nested loops.

Historical Context & Motivation

Since the earliest days of computing, programmers have needed ways to represent information that is naturally organized into rows and columns. Spreadsheets, game boards, images, and seating charts all share a common trait: their data lives on a two-dimensional grid. A single flat list can hold a sequence of values, but it falls short when you need to express the idea that certain values belong to the same row while others belong to the same column. This gap in expressive power led programmers to nest one list inside another, creating what we now call a 2D list (also known as a two-dimensional array or matrix).

1950s
Fortran Introduces Multi-Dimensional Arrays
Fortran, one of the first high-level programming languages, introduced built-in support for multi-dimensional arrays. Scientists used them to represent matrices in physics and engineering calculations.
1970s
C Language and Row-Major Storage
The C programming language stored 2D arrays in row-major order in memory, establishing conventions that many later languages would follow. This made nested-loop traversal a standard pattern.
1991
Python Is Created
Guido van Rossum released Python, which represents 2D data as a list of lists rather than a rigid fixed-size array. This flexible approach makes it easy to build and modify grids on the fly.
2000s–Now
2D Structures Everywhere
From NumPy arrays for data science to pixel grids in image processing and tile maps in game development, 2D data structures remain one of the most commonly used patterns in all of programming.

The central question this lesson addresses is straightforward: How do we store, access, and process data that is organized in rows and columns using Python's list-of-lists structure and nested for-loops? By the end, you will be able to create 2D lists, read individual cells, and loop through every element systematically.

Core Principles & Definitions

Before writing any code, let's lock down the key ideas. A 1D list (one-dimensional list) is a single sequence of values, like [10, 20, 30]. A 2D list is a list whose elements are themselves lists. Each inner list represents one row of the grid. When every inner list has the same length, the 2D list looks exactly like a table with uniform rows and columns.

1

Outer List = Rows

The outer list holds all the rows. Its length tells you how many rows the grid has. You access a specific row using a single index, like grid[0] for the first row.
2

Inner List = Columns

Each inner list contains the column values for that row. The length of an inner list tells you how many columns exist. Access a specific cell with two indices: grid[row][col].
3

Nested Loops for Traversal

An outer loop iterates through each row. An inner loop iterates through each element within that row. Together they visit every cell exactly once—this pattern is called nested iteration.
4

Zero-Based Indexing

Like all Python lists, 2D lists use zero-based indexing. The top-left element is grid[0][0] and the bottom-right is grid[rows−1][cols−1].
✦ KEY TAKEAWAY
Think of a 2D list like a parking garage. The outer list is the list of floors in the garage. Each floor (inner list) has its own row of parking spaces (elements). To find a specific car, you first pick the floor number (row index), then walk to the right space number (column index). A nested loop is like an attendant who visits every single floor and every space on that floor—ensuring no car is missed.

Visual Explanation — The Grid Model

The diagram below shows a 3×4 grid stored as a Python list of lists. The outer list contains three inner lists (rows 0, 1, and 2). Each inner list contains four elements (columns 0 through 3). On the left you see the Python syntax; on the right you see the same data laid out as a visual grid with row and column indices labeled.

The left panel shows the Python code that creates a 3×4 grid. The right panel maps the same data onto a visual table. The dashed cyan box highlights the cell at grid[1][2], which contains 70 (row 1, column 2).

Notice how the first index selects the row and the second index selects the column within that row. This two-step lookup mirrors how you find a seat in a movie theater: first you find your row, then you count across to the correct seat number. The function len(grid) returns the number of rows (3), while len(grid[0]) returns the number of columns in the first row (4).

How Nested Loops Process a 2D List

The most common operation on a 2D list is visiting every element. This is accomplished with a nested loop: an outer loop that iterates over each row, and an inner loop that iterates over each element in that row. There are two flavors you should know—index-based and value-based traversal.

Index-Based Traversal

INDEX-BASED PATTERN
for r in range(len(grid)): for c in range(len(grid[r])): process(grid[r][c])
r = row index (0 to number of rows − 1), c = column index (0 to number of columns in that row − 1). Use this pattern when you need to know the position of each element.

Value-Based Traversal

VALUE-BASED PATTERN
for row in grid: for value in row: process(value)
row = the entire inner list for that row, value = the individual element. This pattern is simpler when you only care about the values, not their positions.

Total Iterations

TOTAL CELL VISITS
total visits = rows × columns
For a grid with 3 rows and 4 columns the inner statement executes 3 × 4 = 12 times. In general, for an m × n grid, the loop body runs m × n times.
⚠️ Common Mistake
Swapping the row and column indices is one of the most common bugs. If you write grid[c][r] instead of grid[r][c], you'll get an IndexError whenever the grid isn't square (same number of rows and columns). Always remember: row first, column second.

Common 2D List Patterns

Beyond simple traversal, several recurring patterns appear when working with 2D lists. The diagram below illustrates four of the most important: summing all elements, finding a maximum value, processing a single row, and processing a single column. Each pattern adapts the nested-loop template in a slightly different way.

Four essential patterns for 2D list processing. Patterns ① and ② require full nested loops. Pattern ③ uses a single loop on one row. Pattern ④ uses a single loop that extracts the same column from every row.
Common 2D list operations and the loop structures they require.
PatternLoops NeededWhen to Use
Sum / Count / Average all cellsNested (2 loops)When the result depends on every element in the grid.
Find max / minNested (2 loops)When searching the entire grid for an extreme value.
Process one rowSingle loopWhen the task targets a specific row only.
Process one columnSingle loopWhen the task targets a specific column across all rows.
Build a 2D listNested (2 loops)When creating a grid dynamically using input or calculations.

Worked Example — Classroom Grade Averages

Suppose you have a 2D list where each row stores one student's test scores across four exams. You want to compute each student's average and then find the overall class average. Let's walk through the solution step by step.

Computing Student and Class Averages
1
Step 1 — Define the 2D listWe store three students' scores as a list of lists. Each inner list holds four exam scores: scores = [[85, 92, 78, 90], [70, 65, 80, 75], [95, 88, 92, 97]]
3 rows (students) × 4 columns (exams)
2
Step 2 — Loop through each row to find student averagesWe use an index-based outer loop so we can label each student. Inside, we sum the row and divide by the number of exams: for r in range(len(scores)): total = 0 for c in range(len(scores[r])): total += scores[r][c] avg = total / len(scores[r]) print(f"Student {r}: {avg}")
Student 0: 86.25, Student 1: 72.5, Student 2: 93.0
3
Step 3 — Compute the class averageTo find the overall class average, sum every cell in the grid and divide by the total number of cells: grand_total = 0 for row in scores: for val in row: grand_total += val num_cells = len(scores) * len(scores[0]) class_avg = grand_total / num_cells
grand_total = 1007, num_cells = 12, class_avg ≈ 83.92
4
Step 4 — Verify the resultA quick sanity check: the individual averages were 86.25, 72.5, and 93.0. Their average is (86.25 + 72.5 + 93.0) / 3 ≈ 83.92, which matches. This confirms our nested loop processed every cell correctly.
Class average = 83.92 ✓

Strengths & Limitations of 2D Lists

Python's list-of-lists approach to 2D data is wonderfully flexible, but it is not the ideal choice in every situation. Understanding its strengths and limitations helps you decide when to use it and when to explore alternatives.

Comparing the strengths and limitations of Python's list-of-lists for 2D data.
StrengthsLimitations
Easy to create and modify — just use familiar list operations like append and indexing.No built-in enforcement that all rows have the same length, which can lead to 'ragged' lists and subtle bugs.
Can hold mixed data types — one cell can be an int, another a string.Slower for large numeric computations compared to optimized libraries like NumPy.
Great for representing game boards, seating charts, simple pixel grids, and tabular data.No built-in matrix operations (transpose, matrix multiplication) — you must code them yourself.
Rows can be different lengths, which is useful for triangular or irregular data.For very large datasets, memory usage is higher than fixed-type arrays.
💡 WHEN TO USE 2D LISTS
For most tasks in an introductory Python course — tic-tac-toe boards, gradebooks, simple image filters — a list of lists is the right tool. It keeps your code readable and avoids extra library dependencies. Once you start working with large numeric datasets or need operations like matrix multiplication, you'll graduate to libraries like NumPy, which is built on the same conceptual foundation but runs much faster.

Connection to Advanced Structures

The 2D list concept you are learning today is the gateway to several more powerful tools that you will encounter as your programming journey continues. The table below maps what you know now to what you'll learn next.

How 2D lists connect to more advanced data structures and tools.
What You Know NowWhat Comes Next
2D list of lists in pure PythonNumPy 2D arrays — fixed-type, fast, with built-in math operations like dot products and transpositions.
Nested for-loops to visit every cellVectorized operations — NumPy lets you add, multiply, or compare entire arrays without writing any loops.
Grid as rows and columns of numbersPandas DataFrames — labeled rows and columns, making tabular data analysis intuitive.
Flat, in-memory storageDatabases and SQL — store millions of rows in tables on disk and query them efficiently.

The skills you build now — thinking in rows and columns, using nested loops, and selecting specific cells with double indexing — will directly transfer to every one of these advanced tools. Mastering the fundamentals makes the jump to professional libraries feel natural rather than overwhelming.

Practice Problems

PROBLEM 1 — CONCEPTUAL
Given the 2D list m = [[5, 10], [15, 20], [25, 30]], what does m[2][0] evaluate to? Explain your reasoning in terms of rows and columns.
PROBLEM 2 — BASIC CALCULATION
Write a nested loop that prints every element of the following 2D list, one value per line: data = [[1, 2, 3], [4, 5, 6]]
PROBLEM 3 — INTERMEDIATE
Given temps = [[72, 75, 78], [68, 70, 73], [80, 82, 85], [65, 63, 67]] where each row represents one day's morning, afternoon, and evening temperatures, write code that calculates and prints the average temperature for each day.
PROBLEM 4 — APPLIED
You are building a tic-tac-toe game. The board is represented as: board = [["X", "O", "X"], ["O", "X", " "], [" ", "O", "X"]] Write a function count_marks(board, mark) that returns how many times a given mark ("X", "O", or " ") appears on the board.
PROBLEM 5 — CRITICAL THINKING
A teacher stores quiz scores in grades = [[88, 92, 79], [95, 85, 100], [70, 78, 82]] where each row is a student and each column is a quiz. Write code that finds the highest average quiz score across all quizzes (i.e., compute the average of each column and return the maximum). Which column has the highest average?

Lesson Summary

A 2D list in Python is a list of lists where the outer list holds rows and each inner list holds the column values for that row. You access a specific cell using double indexing: grid[row][col]. To process every element, use a nested loop — an outer loop for rows and an inner loop for columns. The total number of iterations equals rows × columns.

Key patterns include summing all elements, finding a maximum, processing a single row with one loop, and processing a single column by looping through rows while keeping the column index fixed. You can choose between index-based traversal (using range(len(...))) when you need positions, or value-based traversal (using for item in list) when you only need values. These foundational skills prepare you for advanced tools like NumPy arrays and Pandas DataFrames.

Varsity Tutors • Computer Programming 1 (Python) • 2D Lists