Skip to content
wannacode

An array that grows

Problem

The cart is full

You are writing the shopping cart of an online store, and you keep the items in an array, as in the lesson on arrays: each item at its own index. JavaScript lets you write past the end too, but you have to keep track of the first free index. And when the customer removes an item, a hole is left:

const cart = ["Bread", "Milk", "Coffee"];
// A fourth item: we write at the first free index.
cart[cart.length] = "Butter";
// The customer removes the milk.
cart[1] = undefined;
for (const item of cart) {
console.log(item);
}
console.log(cart.length);
Output
Bread
undefined
Coffee
Butter
4
Now every loop over the cart has to skip undefined. And the number of items is no longer cart.length: that is 4, but there are 3 items.

That is a lot of code for something every store does all the time.

The idea

push and splice: the array looks after itself

The array has methods that do this for you. It does the copying and shifting by itself. A collection that grows and shrinks like this is called a list, and in JavaScript that is the array itself:

const cart = [];
cart.push("Bread");
cart.push("Milk");
cart.push("Coffee");
cart.push("Butter");
cart.splice(cart.indexOf("Milk"), 1);
console.log(cart);
console.log(`Items: ${cart.length}`);
Output
[ 'Bread', 'Coffee', 'Butter' ]
Items: 3
  • push adds at the end.
  • There is no method that removes by value. indexOf finds the index of "Milk", and splice(index, 1) removes one element from there and closes the hole: "Coffee" moves into the place of "Milk".
  • length is how many elements there are right now.
  • console.log prints the whole array in square brackets, with the text in quotes.

Here is what the array does inside. You do not choose how many places it has. The engine that runs the code does (V8 in Node and Chrome). An array of 3 items written in the code has exactly 3 places, so it fills up on the fourth:

const cart = ["Bread", "Milk", "Coffee"];
cart.push("Butter");
cart.splice(cart.indexOf("Milk"), 1);
console.log(cart);
console.log(`Items: ${cart.length}`);
Step by step, in words
  1. cart points to an array. Inside, it has length and a separate elements array with 3 places.
  2. The items from the code fill the places one by one. The array is full: 3 of 3.
  3. There is no room. push makes an array with 22 places and copies the elements into it.
  4. elements now points to the new array, and nobody needs the old one. "Butter" goes into the free place.
  5. splice removes "Milk", and everything after it moves one place to the left. No hole is left.
  6. From the outside you see only an array of three items. The V8 engine decides how many places it has inside: the language does not promise it.
More precisely, if you are curious
  • There is also delete cart[1], but it does not shift anything. It leaves a hole, and length stays the same:
const cart = ["Bread", "Milk", "Coffee"];
delete cart[1];
console.log(cart, cart.length);
Output
[ 'Bread', <1 empty item>, 'Coffee' ] 3

For an array, use splice.

The idea

What goes in an array

An array does not ask what you put in it. Text and a number can sit side by side:

const cart = [];
cart.push("Bread");
cart.push(5);
console.log(cart);
Output
[ 'Bread', 5 ]

That looks handy, but the error shows up later, where you expect text. So one array holds one kind of thing: only items, only prices.

JavaScript itself has no way to say "an array of text only". TypeScript does that. It is JavaScript with types: there, an array of text is string[], and cart.push(5) is an error right in the editor.

The idea

Indexes: [i], splice, indexOf, includes

An array has indexes from 0 to tasks.length - 1. You read and replace with [ ], and you insert with splice:

const tasks = ["March invoice", "Call the bank", "Warehouse report"];
console.log(tasks[0]);
tasks[1] = "Meeting at the bank";
tasks.splice(0, 0, "Coffee with the team");
console.log(tasks.join(", "));
console.log(tasks.indexOf("Warehouse report"));
console.log(tasks.includes("Call the bank"));
Output
March invoice
Coffee with the team, March invoice, Meeting at the bank, Warehouse report
3
false
  • tasks[i] reads, and tasks[i] = x replaces.
  • splice(i, 0, x) inserts at index i and shifts the rest to the right. The zero means "remove nothing".
  • indexOf(x) searches and returns the index or -1, and includes(x) only says whether it is there.
  • join(", ") joins the elements into one piece of text, separated by commas.
More precisely, if you are curious
  • An index outside the array does not stop the program. It gives undefined. at(-1) counts from the back and gives the last element:
const tasks = ["March invoice", "Call the bank", "Warehouse report"];
console.log(tasks[3]);
console.log(tasks.at(-1));
console.log(tasks.indexOf("Payroll"));
Output
undefined
Warehouse report
-1
  • includes and indexOf compare with ===. Text is a value, so they find text that is equal.
  • Inserting and removing at the front are slow in a big array: every element after the index moves by one place. For a million elements, that is a million moves.

Try it

Who is next?

The list of patients waiting at the doctor's. Ivan goes in, and Ana has an appointment, so she takes second place.

What will the program print?

const waiting = ["Ivan", "Maria", "Peter"];
waiting.shift();
waiting.splice(1, 0, "Ana");
console.log(waiting, waiting.length);
Answer

The idea

Looping over the elements

You loop over an array with for...of when you need only the elements, or with an index when you also need the number:

const cart = ["Bread", "Coffee", "Butter"];
for (const item of cart) {
console.log(`- ${item}`);
}
for (let i = 0; i < cart.length; i++) {
console.log(`${i + 1}. ${cart[i]}`);
}
Output
- Bread
- Coffee
- Butter
1. Bread
2. Coffee
3. Butter
More precisely, if you are curious
  • Do not remove elements from an array while you loop over it with for...of. The program does not stop, but it skips elements:
const cart = ["Bread", "Bread", "Milk"];
for (const item of cart) {
if (item === "Bread") {
cart.splice(cart.indexOf(item), 1);
}
}
console.log(cart);
Output
[ 'Bread', 'Milk' ]

The second "Bread" stayed. splice shifted everything to the left, and the loop went on from the next index and never saw it. There is no error, so a miss like this is hard to catch.

  • If you have to remove, loop with an index from the back to the front. That way, removing does not shift the elements you have not checked yet:
const cart = ["Bread", "Bread", "Milk"];
for (let i = cart.length - 1; i >= 0; i--) {
if (cart[i] === "Bread") {
cart.splice(i, 1);
}
}
console.log(cart);
Output
[ 'Milk' ]
In the "Modern JavaScript" module you will see a shorter way that makes a new array with only the elements you want: filter.

The idea

Numbers in an array

An array of scores is like an array of text. The number sits in the array as it is:

const scores = [85, 64, 91];
let total = 0;
for (const score of scores) {
total += score;
}
console.log(`Average: ${total / scores.length}`);
Output
Average: 80

JavaScript prints 80, not 80.0: whole numbers and decimals are the same type for it, number.

More precisely, if you are curious
  • A number in JavaScript is a primitive value, not an object. That is why === compares the values, and two equal numbers are equal. There is also a wrapper, new Number(257), that makes an object, and two objects are different even if they hold the same thing:
const c = Number("257");
const d = Number("257");
console.log(c === d);
console.log(new Number(257) === new Number(257));
Output
true
false

You do not need new Number. Write the numbers directly.

Try it

Find the bug

A theater keeps the taken seats in a array. A customer gives up seat 2, and the program should print [ 12, 7, 5 ]. Instead it prints [ 12, 2, 5 ]. Which line has the bug?

Pick the line with the bug.

More precisely, if you are curious
const seats = [12, 7, 5];
seats.splice(seats.indexOf(2), 1);
console.log(seats);
Output
[ 12, 7 ]

The idea

Sorting and an array that does not change

sort() sorts the array in place. Text comes out in alphabetical order. But numbers do not come out by size:

const cities = ["Sofia", "Plovdiv", "Varna"];
cities.sort();
console.log(cities);
const prices = [30, 5, 12];
prices.sort();
console.log(prices);
prices.sort(function (a, b) {
return a - b;
});
console.log(prices);
Output
[ 'Plovdiv', 'Sofia', 'Varna' ]
[ 12, 30, 5 ]
[ 5, 12, 30 ]

With no rule, sort turns everything into text and compares the text. "12" comes before "30", and "30" before "5", as in a dictionary. So you give numbers a rule: a function that gets two numbers and returns a - b. A negative result means "a comes before b". For now, copy it. In the lesson "Classes inside classes" you will see how to write rules like this.

You make an array that does not change with Object.freeze. It is immutable: you cannot add, remove or sort in it.

const days = Object.freeze(["Mon", "Tue", "Wed"]);
days.push("Thu");
Error
TypeError: Cannot add property 3, object is not extensible

const is not enough for this: it only stops days from pointing to another array, while the array itself can still change.

More precisely, if you are curious
  • Text is sorted by the codes of its characters. Capital letters come before lowercase ones, so "Pear" comes before "apple":
const fruit = ["apple", "Pear", "banana", "Grape"];
fruit.sort();
console.log(fruit);
Output
[ 'Grape', 'Pear', 'apple', 'banana' ]
  • toSorted() returns a new, sorted array and does not touch the old one:
const prices = [30, 5, 12];
const cheapFirst = prices.toSorted(function (a, b) {
return a - b;
});
console.log(cheapFirst, prices);
Output
[ 5, 12, 30 ] [ 30, 5, 12 ]
  • How to sort your own objects, for example products by price, you will see in the lesson "Classes inside classes".

Exercise

Arrays in classes

Solve the tasks in your editor. The array is a private field of the class (#books), and the code below the classes only calls its methods.

1. A bookshelf. A BookShelf class with a addBook(title) method that puts the book at the end, and takeBook(), which returns and removes the first book (or null if the shelf is empty). Add count() and toString(). Add "Under the Yoke", "Tobacco" and "The Iron Candlestick", take one book and print the rest.

Output
Took: Under the Yoke
2 left: [Tobacco, The Iron Candlestick]

2. An event log. An Event class with a date and a description, and an EventLog class with the methods addEvent(date, description) and getEventsBetween(from, to), which returns a array of the events between the two dates, inclusive. The dates are text in the form 2026-03-31. In this form, the text sorts the same way as the dates, so <= and >= compare them correctly. Add four events: 2026-01-15 "Annual stocktake", 2026-03-31 "First quarter report", 2026-05-20 "New coffee supplier", 2026-09-01 "New school year". Print the ones from 2026-03-01 to 2026-06-30.

Output
2026-03-31 First quarter report
2026-05-20 New coffee supplier

3. A notebook. A Note class with a title and content, and a Notebook class with the methods addNote, findNote(title) (returns the note or null), deleteNote(title) (returns true if it deleted something) and count(). Add the notes "OOP", "Collections" and "SQL", find "Collections", delete "OOP" and check that it is gone.

Output
Collections: the array grows by itself
Deleted: OOP
Notes: 2
There is no OOP note.

4. Mental math. A Question class with a text and the right answer, and a MathGame class with a array of questions, an addQuestion method and a start() method. start asks the questions one by one, reads the answer and at the end says how many were right. The questions are 2 + 2, 3 * 4 and 15 - 9.

start reads with ask from the lesson on input, so it is async start(), and the call is await game.start(). A method that waits for input is written this way. For now, copy it.

Input
4
12
7
Output
2 + 2 = Correct!
3 * 4 = Correct!
15 - 9 = Wrong, the answer is 6.
Score: 2 out of 3

More tasks

  • No repeats. Read 6 names and collect them in a array, skipping the ones that are already in it. For the input Ivan, Maria, Ivan, Ana, Maria, Peter the output is [ 'Ivan', 'Maria', 'Ana', 'Peter' ].
  • The most expensive item. A array holds prices. Find the highest one and its index, without Math.max.
  • Reverse the list. Write a function that returns a new array with the same elements in reverse order. Do not change the original.
  • A shopping list. Read the commands add milk, remove milk, list and end, and keep the list between them. The command to remove an item that is not there says "No such item."

Test

Check yourself

1. Why are push and splice handier than writing by index?
2. What does console.log([30, 5, 12].sort()) print?
3. seats is [12, 2, 7, 5]. What is left after seats.splice(2, 1)?
4. tasks has 3 tasks. What does console.log(tasks[3]) print?
5. const days = Object.freeze(["Mon", "Tue"]); What happens at days.push("Wed")?

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