Skip to content
wannacode

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:

const week1 = [42, 38, 51, 47, 60];
const week2 = [45, 40, 49, 52, 63];
const week3 = [39, 44, 50, 48, 58];
const tuesday = week1[1] + week2[1] + week3[1];
console.log(`Tuesday: ${tuesday}`);
Output
Tuesday: 122

Now 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

An array of arrays: row and column

A table in JavaScript is an array of arrays: each element of the outer array is a whole row, and a row is a regular array of numbers. Such an array is called two-dimensional, and often a matrix.

const sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
];
console.log(sales[1][0]);
console.log(`${sales.length} weeks, ${sales[0].length} days`);
Output
45
3 weeks, 5 days
  • Each inner [ … ] is one row. We write them one under the other, so the table is easy to see.
  • 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.length is the count of rows. sales[0].length is the length of the first row, that is, the count of columns.

sales[1] is a whole row, a regular array with 5 numbers. That is why you write two separate brackets [1][0]: first you get the row, then the number in it.

const sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
];
console.log(sales[1]);
console.log(sales);
Output
[ 45, 40, 49, 52, 63 ]
[
[ 42, 38, 51, 47, 60 ],
[ 45, 40, 49, 52, 63 ],
[ 39, 44, 50, 48, 58 ]
]

You make an empty 52 × 5 table full of zeros row by row: 52 times you add a new row of 5 zeros.

const table = [];
for (let week = 0; week < 52; week++) {
table.push(new Array(5).fill(0));
}
table[0][1] = 7;
console.log(table.length, table[0].length, table[0][1], table[1][1]);
Output
52 5 7 0
More precisely, if you are curious
  • Since the rows are separate arrays, they can have different lengths. Such a table is called jagged:
const shifts = [
[8, 8],
[6, 6, 6],
[12],
];
console.log(shifts[1].length, shifts[2].length);
Output
3 1

In it, sales[0].length is not the length of every row. That is why in a nested loop it is safer to write sales[row].length: the length of the row you are going through right now.

  • Don't make the empty table with new Array(3).fill(new Array(5).fill(0)). That way all three places point to one and the same row, like the two names of one array from the last lesson. A change in one row shows in all of them:
const table = new Array(3).fill(new Array(5).fill(0));
table[0][1] = 7;
console.log(table);
Output
[ [ 0, 7, 0, 0, 0 ], [ 0, 7, 0, 0, 0 ], [ 0, 7, 0, 0, 0 ] ]

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 (let week = 0; week < sales.length; week++) {
let line = "";
for (let day = 0; day < sales[0].length; day++) {
line += String(sales[week][day]).padStart(5);
}
console.log(line);
}
Output
42 38 51 47 60
45 40 49 52 63
39 44 50 48 58

The 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.

String(x).padStart(5) turns the number into text and pads it with spaces on the left up to 5 characters. That way the columns line up one under the other. The row is collected in line, and console.log(line) prints it after the inner loop.

Try it

Which cell?

In the grade book each row is a student, and each column is a subject.

What will the program print?

const grades = [
[5, 3, 4, 6],
[3, 4, 6, 5],
[6, 2, 5, 4],
];
console.log(grades[1][2]);
Answer

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 (let week = 0; week < sales.length; week++) {
let total = 0;
for (let day = 0; day < sales[0].length; day++) {
total += sales[week][day];
}
console.log(`Week ${week + 1}: ${total}`);
}
Output
Week 1: 238
Week 2: 249
Week 3: 239

const sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
];
for (let week = 0; week < sales.length; week++) {
let total = 0;
for (let day = 0; day < sales[0].length; day++) {
total += sales[week][day];
}
console.log(`Week ${week + 1}: ${total}`);
}
Step by step, in words
  1. The table: 3 weeks (rows) of 5 days (columns).
  2. week = 0: total starts over, from 0.
  3. The inner loop goes through the days: 42 + 38 + 51 + 47 + 60 = 238.
  4. It prints "Week 1: 238".
  5. week = 1: total starts over, from 0.
  6. The inner loop goes through the days: 45 + 40 + 49 + 52 + 63 = 249.
  7. It prints "Week 2: 249".
  8. week = 2: total starts over, from 0.
  9. The inner loop goes through the days: 39 + 44 + 50 + 48 + 58 = 239.
  10. 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?

const days = ["Mon", "Tue", "Wed", "Thu", "Fri"];
for (let day = 0; day < sales[0].length; day++) {
  let total = 0;
  for (let week = 0; week < sales.length; week++) {
    total += sales[][day];
  }
  console.log(`${days[day]}: ${total}`);
}

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 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:

const row = Number(await ask(""));
const seat = Number(await ask(""));
if (hall[row - 1][seat - 1] === 1) {
console.log("The seat is taken.");
} else {
hall[row - 1][seat - 1] = 1;
console.log(`Booked: row ${row}, seat ${seat}`);
}
Input
3
2
Output
Booked: row 3, seat 2
Input
1
2
Output
The seat is taken.

hall is the hall from the previous card, and the lines for ask() and rl.close() are as in the lesson "Input and output". 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, hall[6] is undefined, and undefined[1] stops the program with TypeError. If they ask for seat 10 in a row that exists, there is no error: hall[2][9] is undefined, and the assignment adds a new seat at the end of the row, so the row now has 10 seats:
const hall = [
[1, 1, 0, 0, 1],
[0, 1, 1, 1, 0],
[0, 0, 0, 1, 1],
];
try {
console.log(hall[7 - 1][2 - 1]);
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
hall[3 - 1][10 - 1] = 1;
console.log(hall[2].length);
Output
TypeError: Cannot read properties of undefined (reading '1')
10

So check the numbers before you use them: row >= 1 && row <= hall.length, 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.

The table
1 2
3 4
Output
Main diagonal: 5
Other diagonal: 5
First row: 3
Second row: 7

2. 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].

The table
42 38 51
45 40 49
Output
42 45
38 40
51 49

Exercise

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?

The maze
0 0 0 0
0 1 0 0
0 0 0 1
1 0 0 0
Output
Paths: 3
Hint

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".

The maze
0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 1 1 1 1
0 0 0 0 0
Output
Shortest path: 16 steps
Hint

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: const dr = [-1, 1, 0, 0]; and const 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 (P for 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

Check yourself

1. What is sales[1] in the sales table?
2. The table sales has 3 weeks (rows) and 5 days (columns). What does sales[0].length give?
3. Which is the last cell of a table with 3 rows and 5 columns?
4. You want the sum of one column (one day across all the weeks). Which index changes in the inner loop?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·