Tables of numbers
Problem
Three weeks, three arrays
A coffee shop records how many coffees it sold each workday. For one week, the array from the last lesson is exactly what you need. For three weeks you make three arrays:
int[] week1 = { 42, 38, 51, 47, 60 };int[] week2 = { 45, 40, 49, 52, 63 };int[] week3 = { 39, 44, 50, 48, 58 };
int tuesday = week1[1] + week2[1] + week3[1];Console.WriteLine("Tuesday: " + tuesday);Tuesday: 122Now the manager asks how much was sold on all the Tuesdays of the past year. That is 52 weeks, so 52 arrays and a sum with 52 terms. And a loop cannot go through week1, week2, week3: those are different names, not indexes.
The data is a table: the weeks are rows, the days are columns. You need an array that is a table.
The idea
A two-dimensional array: row and column
An array in which every value has a row and a column is called a two-dimensional array. It is often called a matrix too.
int[,] sales ={ { 42, 38, 51, 47, 60 }, { 45, 40, 49, 52, 63 }, { 39, 44, 50, 48, 58 }};Console.WriteLine(sales[1, 0]);Console.WriteLine(sales.GetLength(0) + " weeks, " + sales.GetLength(1) + " days");453 weeks, 5 daysint[,]means a table of whole numbers. The comma in the brackets says there are two indexes. Each inner{ … }is one row.sales[1, 0]is the row with index 1 (the second week) and the column with index 0 (Monday). The row comes first, then the column. Counting starts at 0 here too.sales.GetLength(0)is the count of rows,sales.GetLength(1)is the count of columns. The number in the parentheses is which dimension you are asking about: 0 for the rows, 1 for the columns.
A table like this is called a rectangular array: all the rows have the same length.
You create an empty table with its sizes: new int[52, 5] is 52 rows of 5 numbers, all of them 0.
More precisely, if you are curious
C# also has a second kind: the jagged array, int[][]. It is an array of arrays, and each row can have a different length. The indexes go in separate brackets, shifts[1][0], and the length of a row is shifts[1].Length:
int[][] shifts = new int[3][];shifts[0] = new int[] { 8, 8 };shifts[1] = new int[] { 6, 6, 6 };shifts[2] = new int[] { 12 };Console.WriteLine(shifts[1].Length + " " + shifts[2].Length);3 1Use int[,] when the data is a real table (weeks and days, rows and seats in a hall). Use int[][] when the rows have different lengths.
sales.Length on an int[,] is the count of all the cells (3 · 5 = 15), not the count of rows. sales.Rank is the count of dimensions (2).
See it
The whole table on the screen
To go through every cell, you need two loops, one inside the other. You already know nested loops from the stars: the outer one picks the row, the inner one goes through the columns of that row.
for (int week = 0; week < sales.GetLength(0); week++){ for (int day = 0; day < sales.GetLength(1); day++) { Console.Write($"{sales[week, day],5}"); } Console.WriteLine();} 42 38 51 47 60 45 40 49 52 63 39 44 50 48 58The outer loop runs 3 times, once for each week. On each of its rounds the inner one runs 5 times. 15 cells in total, in the order you read a table: left to right, top to bottom.
$"{x,5}" prints the number in a field 5 characters wide, aligned to the right. That way the columns line up one under the other. After each row WriteLine() moves to a new line.
Try it
Which cell?
In the grade book each row is a student, and each column is a subject.
What will the program print?
int[,] grades ={ { 5, 3, 4, 6 }, { 3, 4, 6, 5 }, { 6, 2, 5, 4 }};Console.WriteLine(grades[1, 2]);The first index is the row: 1 is the second student, { 3, 4, 6, 5 }. The second index is the column: 2 is the third subject. The grade is 6. You get 2 if you mix up the row and the column, and 3 if you count from 1.
The idea
Sums by rows and by columns
How many coffees were sold each week? That is the sum of each row. The sum starts at 0 for each new row, so total is inside the outer loop:
for (int week = 0; week < sales.GetLength(0); week++){ int total = 0; for (int day = 0; day < sales.GetLength(1); day++) { total += sales[week, day]; } Console.WriteLine("Week " + (week + 1) + ": " + total);}Week 1: 238Week 2: 249Week 3: 239int[,] sales ={ { 42, 38, 51, 47, 60 }, { 45, 40, 49, 52, 63 }, { 39, 44, 50, 48, 58 }};for (int week = 0; week < sales.GetLength(0); week++){ int total = 0; for (int day = 0; day < sales.GetLength(1); day++) { total += sales[week, day]; } Console.WriteLine("Week " + (week + 1) + ": " + total);}Step by step, in words
- The table: 3 weeks (rows) of 5 days (columns).
- week = 0: total starts over, from 0.
- The inner loop goes through the days: 42 + 38 + 51 + 47 + 60 = 238.
- It prints "Week 1: 238".
- week = 1: total starts over, from 0.
- The inner loop goes through the days: 45 + 40 + 49 + 52 + 63 = 249.
- It prints "Week 2: 249".
- week = 2: total starts over, from 0.
- The inner loop goes through the days: 39 + 44 + 50 + 48 + 58 = 239.
- It prints "Week 3: 239".
How much was sold on Monday, on Tuesday, and so on? That is the sum of each column. The table is the same, only the order of the loops changes: the day is on the outside, the weeks are on the inside. You will finish it on the next card.
Which loop is on the outside decides what you add up. The indexes in sales[week, day] don't move: the row always comes first.
Try it
Sum by days
The outer loop goes through the days, the inner one through the weeks. What goes in the blank?
string[] days = { "Mon", "Tue", "Wed", "Thu", "Fri" };
for (int day = 0; day < sales.GetLength(1); day++)
{
int total = 0;
for (int week = 0; week < sales.GetLength(0); week++)
{
total += sales[, day];
}
Console.WriteLine(days[day] + ": " + total);
}week. The row is the week, so week comes first. The day stays in place while the inner loop goes through the three weeks. For Tuesday you get 38 + 40 + 44 = 122, the same as on the first card, but without 52 names.
Try it
Find the bug
The hall has 3 rows of 5 seats: 1 is taken, 0 is free. There are 7 free seats, but the program says 5. Which line has the bug?
Pick the line with the bug.
The inner loop is for the seats in the row, and their count is hall.GetLength(1) (5). hall.GetLength(0) is the count of rows (3), so the last two seats in each row are never checked. The compiler cannot see a bug like this: both numbers are int.
The idea
Booking: people count from 1
A moviegoer wants row 3, seat 2. For them the first row is row 1. For the array it is row 0. That is why you subtract 1 from the numbers a person types before you use them as indexes:
int row = int.Parse(Console.ReadLine());int seat = int.Parse(Console.ReadLine());if (hall[row - 1, seat - 1] == 1){ Console.WriteLine("The seat is taken.");}else{ hall[row - 1, seat - 1] = 1; Console.WriteLine("Booked: row " + row + ", seat " + seat);}32Booked: row 3, seat 212The seat is taken.hall is the hall from the previous card. You change a cell like a regular variable: hall[row - 1, seat - 1] = 1;.
More precisely, if you are curious
- If someone asks for row 7, index 6 is outside the table and the program stops with
IndexOutOfRangeException, just like with a regular array. Check the numbers before you use them:row >= 1 && row <=hall.GetLength(0), and the same for the seat.
- Here each number is on its own line. How to read a whole line of numbers at once (
0 1 1 0), you will see in the next lesson, on text.
Exercise
Working with a table
Solve the exercises in your editor. Write the tables straight into the code.
1. Diagonals and rows. For the 2 × 2 table below, find the sum along the main diagonal (from top left down to bottom right), along the other diagonal, and the sums of the first and the second row. On the main diagonal the row index and the column index are equal. On the other diagonal the column is n - 1 - row.
1 23 4Main diagonal: 5Other diagonal: 5First row: 3Second row: 72. Flip the table. The sales are recorded by weeks (rows) and days (columns). Accounting wants them by days and weeks. Make a new table in which the rows become columns. This is called transposing. The new table has the size columns × rows, and cell [r, c] goes to [c, r].
42 38 51 45 40 49 42 45 38 40 51 49Exercise
Mazes and treasure
3. How many paths are there? The maze is a table: 0 is a passage, 1 is a wall. You start from the top left corner and get to the bottom right one, moving only right or down. How many different paths are there?
0 0 0 00 1 0 00 0 0 11 0 0 0Paths: 3Hint
Make a second table ways with the same size. In it, write how many ways there are to reach each cell. There is 1 way to reach the start. There are 0 ways to reach a wall. Every other cell is reached either from above or from the left, so the count is the sum of the cell above it and the cell to its left (if they exist). Fill in the table row by row, from top to bottom. The answer is in the last cell.
4. The shortest path. A hard one. Now you can move in all four directions: up, down, left and right. What is the smallest number of steps to get from the top left to the bottom right corner? If there is no path, print "No path".
0 0 0 0 01 1 1 1 00 0 0 0 00 1 1 1 10 0 0 0 0Shortest path: 16 stepsHint
Send out a "wave" from the start. Make a table dist full of -1 (we haven't reached that cell yet), and put 0 at the start. Then repeat: find all the cells whose dist equals the current step, and for their free neighbors that we haven't reached yet, write step + 1. The wave grows by one step on every round. Stop when it reaches the goal or when nothing has changed (then there is no path). The four directions are handy as two arrays: int[] dr = { -1, 1, 0, 0 }; and int[] dc = { 0, 0, -1, 1 };. The neighbor in direction k is at row r + dr[k] and column c + dc[k].
More exercises
- Find the treasure. A game on a map that is a table: 0 is a path, 1 is an obstacle, 2 is the treasure. The player starts in the top left corner. On each move, print the map (
Pfor the player,#for an obstacle,.for a path) and read a command as a number: 8 up, 2 down, 4 left, 6 right (as on the number pad). If the move leads off the map or into an obstacle, the player stays in place. When they step on the treasure, print "You found the treasure in … moves!". - The row with the most sales. For the table
sales, find which week was the strongest and which day of the week is the strongest on average. - Free seats in a row. For the hall
hall, find a row that has at least 3 free seats next to each other, and print the number of the row and of the first seat.
Test