LEARN TO CODE | JAVASCRIPT | 02 FUNCTIONS AND COLLECTIONS

02.2 Arrays and array methods

Store an ordered collection, access its elements and choose array methods that match the task without accidentally changing the original data.

01 | A COLLECTION DESERVES ONE NAME

A quiz might have five scores. You could create score1, score2 and score3, then keep adding names. But how would you process any number of scores with the same loop?

An array holds an ordered collection of elements. Each element has a numbered position called an index. The collection has one name, while indexing lets you work with individual elements.

We will use ordinary dense arrays, with consecutive positions. JavaScript can represent more unusual sparse arrays, but leaving gaps is unnecessary for these beginner tasks.

Keep index.html and script.js in the same VS Code folder. Put the current example into script.js, save it, open or refresh index.html in your browser and look in that page’s Developer Tools Console.

Replace the previous example unless a task explicitly says to add to it. Pasting several independent examples together can redeclare the same let or const name. The downloadable examples use a separate folder for each complete program.

02 | INDEXES START AT ZERO

const scores = [8, 5, 9];
console.log(scores[0]);
console.log(scores[2]);
console.log(scores.length);
console.log(scores[3]);
Three values, indexes zero to two
Index Element
0 8
1 5
2 9

Square brackets create an array literal and also access an indexed element. scores.length is a property giving the length, not a function call, so it has no parentheses.

For this dense array, the last existing index is length - 1. Reading scores[3] does not produce a fourth score: it returns undefined. If you then try to use that missing value in another operation, it can cause a later problem.

03 | CONST DOES NOT FREEZE THE CONTENTS

const scores = [8, 5, 9];
scores[1] = 7;
console.log(JSON.stringify(scores));

The binding scores still refers to the same array, but one element has changed. const prevents reassigning scores to a different value; it does not make the array’s contents immutable.

JSON.stringify is used in these console examples to produce a simple text snapshot of the data. Browser object displays can be expandable and may be inspected after later changes; snapshots make expected output easier to compare.

Keep indexes within the intended range. Assigning to a much larger index can create empty slots and change length unexpectedly.

04 | ADD AND REMOVE AT THE ENDS

const clubs = ["Robotics", "Art"];
const newLength = clubs.push("Coding");
console.log(newLength);
console.log(clubs.join(", "));
const removed = clubs.pop();
console.log(removed);
console.log(clubs.join(", "));

A method is a function accessed as a property, such as clubs.push(...). push changes the array by adding at its end and returns the new length. pop changes the array by removing the last element and returns that element.

join creates a string by joining elements with a separator; it does not remove them. For the beginning of an array, unshift adds and shift removes. A pop or shift on an empty array returns undefined.

Do not write clubs = clubs.push("Coding"). push returns a number, not the updated array, and reassigning a const binding would be an additional error.

05 | VISIT EACH ELEMENT

const scores = [8, 5, 9];
let total = 0;
for (const score of scores) {
  total += score;
}
console.log(total);

for (let index = 0; index < scores.length; index++) {
  console.log(`${index}: ${scores[index]}`);
}

for...of supplies an element value on each pass. Use it when the value is what you need. The numeric for loop exposes the index as well, useful for numbering output or updating a particular position.

The condition uses index < scores.length. With length 3, indexes 0, 1 and 2 are valid. <= would add an unwanted pass reading undefined at index 3.

In the for...of loop, const score is a new iteration binding each pass. It is not one const binding being illegally reassigned by your code.

TRY IT YOURSELF

Calculate the average of [8, 5, 9] and explain why an empty array needs a decision before division.

Reveal guidance

The sum is 22 and the average is 22 / 3. For an empty list there is no score average to report; check length and choose an explicit message instead of dividing by zero.

06 | SLICE COPIES; SPLICE CHANGES

const names = ["Alex", "Sam", "Robin", "Jo"];
const middle = names.slice(1, 3);
console.log(middle.join(", "));
console.log(names.length);
const removed = names.splice(1, 1);
console.log(removed.join(", "));
console.log(names.join(", "));

slice(start, end) produces a new array from start up to, but excluding, end. It leaves the source array unchanged. slice(1, 3) selects indexes 1 and 2.

splice(start, deleteCount) changes the source array. Here it removes one element at index 1 and returns the removed elements as an array. The similar names hide an important difference, so check which operation you intend.

These are shallow operations: if an array contains objects, copying the array does not automatically make independent copies of those objects. The next page demonstrates that distinction.

07 | WHAT IS A CALLBACK?

Some methods need a small piece of behaviour from you. To double every score, the method needs to know what “double” means. You supply a function that the method calls for each relevant element.

That supplied function is a callback . A callback is not automatically asynchronous; the array methods here call their callbacks during the method call.

function doubleScore(score) {
  return score * 2;
}
const scores = [2, 5, 8];
const doubled = scores.map(doubleScore);
console.log(doubled.join(", "));

map receives doubleScore, the function value, without calling it immediately. map then calls it with each score. Writing doubleScore() instead would call it now, without the intended score, and pass its result rather than the function.

08 | MAP TRANSFORMS; FILTER SELECTS

const scores = [2, 5, 8];
const doubled = scores.map((score) => score * 2);
const qualifying = scores.filter((score) => score >= 5);
console.log(doubled.join(", "));
console.log(qualifying.join(", "));
console.log(scores.join(", "));

The arrow functions are the callbacks. map uses each returned value as an element in a new array. filter uses the callback’s truthy/falsy result to decide whether to include each source element.

For a dense source array, map keeps the same number of elements. filter may keep fewer, none, or all. These callbacks do not modify their source, so scores remains unchanged.

If you add braces to a callback, include return when you need a returned result: (score) => { return score * 2; }. Omitting it would make map collect undefined results.

09 | FIND ONE VALUE OR ASK A YES/NO QUESTION

const scores = [2, 5, 8];
console.log(scores.find((score) => score >= 5));
console.log(scores.find((score) => score > 10));
console.log(scores.includes(5));
console.log(scores.includes("5"));

find returns the first matching element, or undefined when none matches. filter returns an array of every matching element. includes asks whether a value occurs and returns a boolean; the number 5 and text "5" differ.

Check a possibly missing result before using its properties. When later searching an array of objects, undefined means you cannot immediately read found.name safely.

TRY IT YOURSELF

Which method should produce every score at least 5, and which should produce only the first?

Reveal guidance

Use filter for all matching scores and find for the first. Use includes when checking whether one particular value is present.

10 | SORT NUMBERS WITH A NUMERIC RULE

const scores = [3, 20, 8];
const sorted = scores.slice().sort((a, b) => a - b);
console.log(sorted.join(", "));
console.log(scores.join(", "));

sort changes the array it is called on. slice() first makes a shallow array copy, so the original order stays available.

Without a comparator, sort uses string-based ordering, so ordinary numeric ascending order is not guaranteed. For our finite numbers, (a, b) => a - b puts a before b when the result is negative, and after b when positive.

The comparator returns an ordering number, not simply true or false. For descending order, use b - a. Later you can compare numeric properties of objects in the same way.

11 | EXPLORE THE METHOD RESULTS

Every choice starts from a fresh [2, 5, 8]. Predict the result and whether the source changes.

Choose an operation, then check.
Keep mutation deliberate
Changes the source in our examples Produces a new array without itself changing the source
push, pop, shift, unshift, splice, sort slice, map, filter

“Does not itself change the source” is not a promise that any callback is harmless: a callback can contain its own mutations. Our callbacks simply return calculations or comparisons.

KEEP LEARNING

Previous page 02.3 Objects and properties

Download Section 02 example programs

Further reading: Functions · Arrays · Objects · VS Code browser debugging