7.

Arrays

What’s more common than dealing with a single primitive value, like a number, is dealing with a collection of values. That’s where arrays come in. Arrays are values that are sort of like strings, but instead of holding a list of characters, they can hold a list of any values. We often refer to the values in arrays as elements. If we have an array of values, we can treat those values as a collection and deal with them all at once. Arrays always have an order: from left to right. Just like writing down a list of groceries, there is an order (top-to-bottom), even if you don’t care about it or use it. This may sound obvious, but there are other common data structures that are not ordered, such as objects and sets.

Create

To create an array, we can use square brackets to surround a bunch of values separated by commas, like so: [2, 3, 4, 5].

console.log([2, 3, 4, 5]); // print the array

Naturally, an array itself is a value (even though it holds multiple values inside of it), so we can assign it to a variable.

const evenNumbers = [2, 4, 6, 8, 10];
console.log(evenNumbers);

Of course, we don’t have to limit ourselves to numbers. Any values work in arrays!

const fruits = ['apple', 'banana', 'cherry'];
console.log(fruits);

const booleans = [true, false];
console.log(booleans);

JavaScript allows us to put different types of values in the same array, although it may not be useful.

const mix = [1, 2, 'apple', true, undefined, null, false, -1000];
console.log(mix);

We can even put arrays in arrays. This is often useful!

const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(matrix);

If we need to create an array and we have a size in mind but not values, sort of like an empty box to fill later, we can use the following syntax as a shortcut.

const xs = [...Array(10)]; // Creates an array of 10 elements, all undefined

This will come in handy later.

Index

Because an array is a list of values, we often need to get one element from that list. Like strings, we can get an element by its index number. Remember that index numbers start from 0, not 1.

const fruits = ['apple', 'banana', 'cherry'];
console.log(fruits[0]); // apple
console.log(fruits[1]); // banana
console.log(fruits[2]); // cherry

This also works for nested arrays.

const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(matrix[1]); // [4, 5, 6]
console.log(matrix[1][0]); // 4

Length

Also like strings, arrays have the length property.

const empty = [];
console.log(empty.length); // 0

const little = [0];
console.log(little.length); // 1

const evenNumbers = [2, 4, 6, 8, 10];
console.log(evenNumbers.length); // 5

const fruits = ['apple', 'banana', 'cherry'];
console.log(fruits.length); // 3
console.log(fruits[fruits.length - 1]); // cherry

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];
console.log(matrix.length); // 3
console.log(matrix[matrix.length - 1]); // [7, 8, 9]
console.log(matrix[0].length); // 3 -- because the first row of the matrix is an array of 3 elements

Gotcha! Equality

JavaScript has quite a few “gotchas”, or quirks that don’t work as you would expect them to.

Checking if two arrays are equal (i.e., the same values) is counter-intuitive in JavaScript. Let’s look at an example.

const numbers = [1, 2, 3];
const sameNumbers = [1, 2, 3];
console.log(numbers === numbers); // true
console.log(numbers === sameNumbers); // false!

What? How could this be false? The two arrays are clearly identical. It turns out that JavaScript’s equality operator (===) is not checking intuitive equality when it comes to arrays. It’s checking whether they are the same exact variable. The bottom line is, don’t try to compare arrays with the equality operator (===). In a real project, if you need to check if two arrays are equal, use a library like Ramda or Underscore.js.

Methods

Arrays have many methods to make use of.1 Here we will highlight a few of the most useful ones.

includes()

The includes() method lets us check if a specific value is included in an array, returning true or false.

const empty = [];
console.log(empty.includes(0)); // false

const little = [0];
console.log(little.includes(0)); // true
console.log(little.includes('anything')); // false

const evenNumbers = [2, 4, 6, 8, 10];
console.log(evenNumbers.includes(4)); // true
console.log(evenNumbers.includes(3)); // false

const fruits = ['apple', 'banana', 'cherry'];
console.log(fruits.includes('banana')); // true
console.log(fruits.includes('app')); // false
console.log(fruits.includes('Apple')); // false
console.log(fruits[0].includes('app')); // true -- using string includes()
console.log(fruits.includes('donut')); // false

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];
console.log(matrix.includes([1, 2, 3])); // false -- because of the equality gotcha!
console.log(matrix[2].includes(9)); // true

slice()

The slice() method lets us grab just a portion of an array. The first argument is the index to start from. The optional second argument is the index to stop at (without the second argument, it goes until the end). It returns an array containing the elements between. It will always return an array, even if it’s empty.

const empty = [];
console.log(empty.slice(0)); // []

const little = [0];
console.log(little.slice(1)); // []

const evenNumbers = [2, 4, 6, 8, 10];
console.log(evenNumbers.slice(1)); // [4, 6, 8, 10]
console.log(evenNumbers.slice(2, 3)); // [6]

const fruits = ['apple', 'banana', 'cherry'];
console.log(fruits.slice(2)); // ['cherry']

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];
console.log(matrix.slice(1, 2)); // [[4, 5, 6]]
console.log(matrix.slice(1, 2)[0].slice(0, 1)); // [4]

sort()

It’s often that we have a list of numbers, words, or other things that need to be sorted into order. The sort() method lets us do exactly that!

const someNumbers = [3, 99, 0, -6, 10];
console.log(someNumbers.sort()); // [ -6, 0, 10, 3, 99 ]

const fruits = ['banana', 'cherry', 'fig', 'apple', 'date', 'elderberry'];
console.log(fruits.sort()); // [ 'apple', 'banana', 'cherry', 'date', 'elderberry', 'fig' ]

reverse()

We can use reverse() to reverse the order of an array.

const someNumbers = [3, 99, 0, -6, 10];
console.log(someNumbers.reverse()); // [ 10, -6, 0, 99, 3 ]
console.log(someNumbers.sort().reverse()); // [ 99, 3, 10, 0, -6 ]

const fruits = ['banana', 'cherry', 'fig', 'apple', 'date', 'elderberry'];
console.log(fruits.sort().reverse()); // [ 'fig', 'elderberry', 'date', 'cherry', 'banana', 'apple' ]

Gotcha! Sneaky Methods

The sort() and reverse() methods do something sneaky that you probably didn’t realize. They change the original array. That means, after using sort() or reverse() on an array, you no longer have the original order! You can’t stop them from doing this, but being aware of it can help you track down tricky bugs caused by it.

const someNumbers = [3, 99, 0, -6, 10];
console.log(someNumbers); // [ 3, 99, 0, -6, 10 ]
console.log(someNumbers.reverse()); // [ 10, -6, 0, 99, 3 ]
console.log(someNumbers); // [ 10, -6, 0, 99, 3 ] -- not the original order!
console.log(someNumbers.sort()); // [ -6, 0, 10, 3, 99 ]
console.log(someNumbers); // [ -6, 0, 10, 3, 99 ] -- updated again!

We can know that sort() behaves this way because the MDN documentation says it sorts the array in place.

Adding to Arrays

Now that we have seen how to access elements and parts of an array, what about adding more elements to an array? If you search online something like “javascript array add element” you’ll probably discover the push() method. While that method technically works, it goes against the immutability principle of functional programming.2 Instead, when we think about adding elements to an array, or modifying an array in any way, we should think of producing a modified copy of the array.

Now if we take a guess at the syntax, we might try something like the following.

const someNumbers = [1, 2, 3];
const moreNumbers = [someNumbers, 4, 5, 6]; // we want: [1, 2, 3, 4, 5, 6]
console.log(moreNumbers); // [ [ 1, 2, 3 ], 4, 5, 6 ]

But this doesn’t achieve the intended result. The array someNumbers is treated as a single value, put inside a new array, followed by the next few numbers.

We need some new syntax: the spread operator: ... . We can spread out an array’s elements as separate values.

const someNumbers = [1, 2, 3];
const moreNumbers = [...someNumbers, 4, 5, 6];
console.log(moreNumbers); // [ 1, 2, 3, 4, 5, 6 ]

The spread operator can be used on any array. Adding arrays together becomes very easy.

console.log([...[1, 2, 3], ...[4, 5, 6], ...['apple', 'banana', 'cherry']]); // [ 1, 2, 3, 4, 5, 6, 'apple', 'banana', 'cherry' ]

// Watch what happens without spread
console.log([ [1, 2, 3], [4, 5, 6], ['apple', 'banana', 'cherry'] ]); // [ [1, 2, 3], [4, 5, 6], ['apple', 'banana', 'cherry'] ]

Adding a new element to an array is easy now. We can even add it to the start or the end.

const fruits = ['banana', 'cherry'];

const moreFruits = ['apple', ...fruits];
console.log(moreFruits); // [ 'apple', 'banana', 'cherry' ]

const evenMoreFruits = [...moreFruits, 'date'];
console.log(evenMoreFruits); // [ 'apple', 'banana', 'cherry', 'date' ]

With a bit of cleverness, we can even add an element somewhere in the middle!

const someNumbers = [1, 2, 3, 5, 6, 7];
const moreNumbers = [...someNumbers.slice(0, 3), 4, ...someNumbers.slice(3)];
console.log(moreNumbers); // [ 1, 2, 3, 4, 5, 6, 7 ]

Or replace the first element.

const someNumbers = [99, 2, 3, 4];
const betterNumbers = [1, ...someNumbers.slice(1)];
console.log(betterNumbers); // [ 1, 2, 3, 4 ]

In fact, the spread operator can be used to copy arrays so the sort() and reverse() gotcha doesn’t affect us!

const someNumbers = [3, 99, 0, -6, 10];
const fakeCopyNumbers = someNumbers; // not a real copy, only a reference
const copyNumbers = [...someNumbers]; // exact copy of someNumbers

console.log(someNumbers); // [3, 99, 0, -6, 10] -- original order

console.log(someNumbers.sort()); // [ -6, 0, 10, 3, 99 ] -- sorted

console.log(someNumbers); // [ -6, 0, 10, 3, 99 ] -- not the original order!
console.log(fakeCopyNumbers); // [ -6, 0, 10, 3, 99 ] -- not the original order because it's a fake copy!
console.log(copyNumbers); // [3, 99, 0, -6, 10] -- still has the original order because it's a copy

The most common real-world uses of adding to arrays are: adding an element to the end of an array, and adding two arrays together.

const teachers = ['Alice', 'Bob', 'Carol'];
const newTeachers = [...teachers, 'Dave']; // add the new teacher Dave
console.log(newTeachers); // [ 'Alice', 'Bob', 'Carol', 'Dave' ]

const managers = ['Eve', 'Frank', 'Grace'];
const staff = [...newTeachers, ...managers]; // gather all staff together
console.log(staff);// [ 'Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank', 'Grace' ]

Exercises


  1. See all the array methods on MDN.↩︎

  2. The push() method will add an element to the operating array, directly modifying it. Once this is done, our original array variable declaration is no longer true. For instance:

    const someNumbers = [1, 2, 3];
    console.log(someNumbers);
    someNumbers.push(4);
    console.log(someNumbers);
    // Here, someNumbers = [1, 2, 3] is not true.
    ↩︎
Top