Lists and Arrays

Suppose you want to store the test scores of everyone in your class. With what you know so far you might reach for a separate variable for each pupil: score1, score2, score3… For thirty pupils that's thirty names to invent, thirty lines to type, and no easy way to say "add them all up". It doesn't scale.

The fix is one of the most useful ideas in all of programming: an array (also called a list). An array stores many values under a single name — think of a row of numbered pigeonholes, or a train of carriages, all sharing one label. Instead of thirty variables you have one, called scores, holding all thirty values in order:

const scores = [72, 85, 90, 64, 88];

The square brackets [ ] make an array; the values inside, separated by commas, are its elements. This one array does the job of five separate variables — and the same idea works just as well for five thousand.

Reaching in: the index

Each element has a numbered position called its index, and you fetch an element by writing the array's name followed by the index in square brackets: scores[2]. There is one thing that surprises everyone at first — the counting starts at zero. The first element is at index 0, the second at index 1, and so on. So an array of five things has indices 0, 1, 2, 3, 4:

The little property .length tells you how many elements an array holds. Run this and watch how the indices line up:

const scores: number[] = [72, 85, 90, 64, 88]; console.log("The whole array:", scores); console.log("How many scores?", scores.length); // 5 console.log("First score, scores[0]:", scores[0]); // 72 console.log("Third score, scores[2]:", scores[2]); // 90 console.log("Last score, scores[4]:", scores[4]); // 88

Notice the tidy trick in the last line: because the length is 5 but the indices stop at 4, the last element always sits at index length - 1. That's why scores[scores.length - 1] is the universal way to grab the final element, whatever the array's size.

Looping over an array

The real power appears when you combine an array with a count-controlled loop. Let the counter i run from 0 up to (but not including) length, and on each pass scores[i] is the next element. One little loop visits every item, no matter how many there are:

const scores: number[] = [72, 85, 90, 64, 88]; for (let i = 0; i < scores.length; i++) { console.log("Score at index " + i + " is " + scores[i]); }

Read the condition carefully: i < scores.length, using <, not <=. Because the indices run 0 to length - 1, stopping before length is exactly right — it visits each element once and no more. (Getting that comparison wrong is such a common slip that it has its own warning further down.)

Working the whole array: total, average, highest

Looping lets you process every element to work out a single answer about the whole collection. Keep a running total as you go (an accumulator, just like summing 1 to n), and the total plus the length gives you the average too:

const scores: number[] = [72, 85, 90, 64, 88]; let total: number = 0; for (let i = 0; i < scores.length; i++) { total = total + scores[i]; // add each score onto the total } const average = total / scores.length; console.log("Total: " + total); console.log("Average: " + average);

Finding the highest score is a lovely pattern. Start by assuming the first element is the biggest, then walk through the rest; whenever you meet something larger, remember it instead. By the end, your "biggest so far" really is the biggest:

const scores: number[] = [72, 85, 90, 64, 88]; let highest: number = scores[0]; // best guess so far: the first one for (let i = 1; i < scores.length; i++) { if (scores[i] > highest) { highest = scores[i]; // found a bigger one — remember it } } console.log("The highest score is " + highest);

The same shape (with the comparison flipped to <) finds the lowest. This "carry the best-so-far through a loop" idea shows up everywhere once you start looking.

Counting things that match

Loops and arrays also answer questions like "how many passed?" Keep a counter, and add one every time an element meets your condition. Here the pass mark is 70:

const scores: number[] = [72, 85, 90, 64, 88]; let passes: number = 0; for (let i = 0; i < scores.length; i++) { if (scores[i] >= 70) { passes = passes + 1; // one more pupil passed } } console.log(passes + " out of " + scores.length + " passed.");

Arrays aren't just numbers — and they can grow

An array can hold text (strings) just as happily as numbers — a shopping list, say. And an array isn't frozen at the size you first gave it: .push(value) adds a new element to the end, and .length grows to match. Watch the list get longer:

const shopping: string[] = ["bread", "milk", "eggs"]; console.log("Start with " + shopping.length + " items:", shopping); shopping.push("cheese"); // add one to the end shopping.push("apples"); // and another console.log("Now " + shopping.length + " items:", shopping); for (let i = 0; i < shopping.length; i++) { console.log((i + 1) + ". " + shopping[i]); // a numbered list for humans }

See how the printed list numbers items 1, 2, 3, \dots for the human by writing i + 1, even though the computer stores them at indices 0, 1, 2, \dots. Machines count from zero; people count from one; a good program quietly bridges the gap.

It looks like a nuisance, but there's a neat reason. Deep down, an array lives in a run of memory slots one after another, and the index is really "how far from the start". The very first element is the start, so it's zero steps along — index 0. The second is one step along, and so on. Early languages like C exposed this directly, and almost every language since (TypeScript, Python, Java…) kept the habit. Once you accept "the index is the distance from the front", zero-based counting stops feeling strange and starts feeling right.

The classic array mistake is running off the end. An array of length 5 has valid indices 0, 1, 2, 3, 4 — the last one is length - 1, not length. Asking for scores[5] doesn't crash in TypeScript; it quietly hands back undefined, which then poisons any sum or comparison it touches:

const scores = [72, 85, 90, 64, 88]; // length 5, indices 0..4 console.log(scores[4]); // 88 ✓ the real last element console.log(scores[5]); // undefined ✗ there is no index 5!

The same trap hides in loops. Writing i <= scores.length (with <=) makes the counter reach 5 on its final pass and read the phantom scores[5]. The safe habit is always i < scores.length with a strict <, and to reach the last element by scores[scores.length - 1]. Off-by-one at the end of an array is one of the most common bugs there is — check it first.