Introduction

We first got introduced to the concept of arrays all the way back in the JavaScript Data Types chapter. There, very briefly, we saw what exactly is an array; how to create one; how to add stuff to an array; how to sort elements; and much more.

Now all that information was brief, and only sufficient enough to give you head start on the array reference type.

Arrays in JavaScript are full of tons and tons of concepts and applications, some of which require very careful understanding. We have things like mutability, dimensions, sparse vs. dense arrays, dynamicity, instance checks and much more.

Arrays are an extremely elementary concept in JavaScript and programming, in general, knowing which will allow you to work more effectively in your programs and be able to implement many many common algorithms out there.

Let's dive into the learning.

What are arrays?

Let's start by reviewing what exactly is an array, before moving to explore the array of concepts related to arrays.

At the core level:

An array is an ordered sequence of items.

The idea of an array is quite similar to the idea of a string. A string is a sequence of textual characters while an array is a sequence of items.

An array could be thought of as a list of values. Let's say you were to store the list of numbers you obtained in recent exams, or the list of items to buy from the grocery store — in each case, what you'd need is an array.

We'll see how to create arrays to store these pieces of information later on in the next section.

Some people also define an array as an ordered collection of items. This is simply the definition of a sequence.

Anyways, moving on, as with all sequences, each item in a given array sits at a given position. This is referred to as its index. In programming, indexes typically begin at 0.

Hence, the first item of an array is at index 0, the second one is at index 1, the third one is at index 2, and so on and so forth.

The total number of items in an array is referred to as the length of the array.

So if an array contains three items, its length would simply be equal to 3.

These concepts shouldn't be new to you. You learnt similar ideas when studying strings in JavaScript. The concept of indexes and length are native to all sequences in JavaScript (and programming, in general). Since arrays are sequences, they share these concepts as well.

Alright, so with this basic theory in mind, let's now see how to create an array in JavaScript.

Array literals

Creating an array in JavaScript is as easy as creating a number, string, or Boolean — thanks to array literals.

A pair of square brackets [] is the literal way to denote an array. Inside the brackets, we put the individual elements of the array, separated by commas from other elements.

In general form, we could represent an array literal as follows:

[item1, item2, ..., itemN]

Each identifier — item1, item2, all the way up to itemN — represents an item stored in the array.

Let's go on and create an array holding the three numbers 1, 2, and 3:

var nums = [1, 2, 3];

Easy, wasn't this?

Logging an array in the console gives a special result — there is a small arrow displayed before the array in the output.

Shown below is an example of logging the nums array created above:

Notice the small arrow before the array?

Clicking this button shows the complete list of items in the array next to their respective indices, as follows:

Throughout this course, when representing arrays in the console, we'll just show the actual value without any arrows. Something like the following:

nums
[1, 2, 3]

Coming back to the array nums, let's add its starting two numbers and see the result. But wait...

For this, we'll need to access the first and second element of nums.

How to do so?

To access an array element in JavaScript, we use what's called bracket notation. We begin with the array, followed by a pair of square brackets ([]). Inside these, we put the index of the element desired to be accessed.

In general notation, this could be expressed as follows:

arr[index]

arr is the array and index is the index of the element wished to be retrieved.

Alright, now that we know how to access an array element, it's time to add the first two numbers of nums. This is accomplished as follows:

var nums = [1, 2, 3];

console.log(nums[0] + nums[1]);
3

The first number has index 0, hence we use nums[0] to access it. Similarly, the second number is at index 1, hence we use nums[1] to access it.

nums[0] evaluates to 1 and nums[1] evaluates to 2, hence nums[0] + nums[1] is the same as 1 + 2, which then evaluates down to 3.

What if now we want to change the second number from 2 to 20?

Can we do that in a JavaScript array?

A big yes!

To modify an exisiting array element, we use the same bracket notation we saw earlier above, but in the context of an assignment. That is, the bracket notation is followed by an equals sign (=) followed by the new value to put at the desired index.

In general form, this could be expressed as:

arr[index] = newValue

arr is the array, index is the index of the element you wish to access and newValue is the new value you want to put in the arr[index].

Let's see a quick modification example:

var nums = [1, 2, 3];
nums[1] = 20;

console.log(nums[1]);
20

As can be seen in the first log here, after executing nums[1] = 20, the second item of nums is no longer 2 — rather it is equal to the new value 20.

Next, we add this number to the third number in nums and see its return value.

nums[1] + nums[2]
23

As expected, it's 23.

So at this point, we know how to create an array, put stuff in it and then later on change that stuff. A couple of things still left to be covered are how to add more elements to an array and remove given elements.

Adding stuff

There is more than just one way to add an element to an array. In this section, we'll cover the two most common ways — using bracket notation and push().

The rest of the ways are usually more specific to given applications. We'll discuss them in the next chapter.

Bracket notation

Many programming languages won't allow us to access an array index greater than or equal to the size of the array. For instance, in Python, doing so would throw an error of type IndexError.

JavaScript is however, loosely-checked in many sectors — and array indices is one of these.

It's not invalid to access an array index that is greater than or equal to the array's length in JavaScript. In normal element access context, such an expression returns undefined, while in the context of an assignment, it adds a new element at the specified position.

This means that to add an element to (the end of) an array, we could simply use something as follows:

arr[arr.length] = value;

arr.length is 1 greater than the last index of arr and so assigning a value to this index would add that value exactly at the end of arr.

Shown below is an example:

var nums = [1, 2, 3];

nums[nums.length] = 10; // nums is now [1, 2, 3, 10]
nums[nums.length] = 20; // nums is now [1, 2, 3, 10, 20]
nums[nums.length] = 30; // nums is now [1, 2, 3, 10, 20, 30]

console.log(nums);
[1, 2, 3, 10, 20, 30]

We start by creating an array nums and then add three new items. Each item is added by assigning a value to the index nums.length, which is next to the last element in the array.

Note that it's not required to use the expression arr.length as the index to add a new element to arr — if we know the last index of the array beforehand, we can even manually pass on the next index manually.

In the code below, we do the same thing as we did above except for that now we manually pass the new indices:

var nums = [1, 2, 3];

nums[3] = 10;
nums[4] = 20;
nums[5] = 30;

console.log(nums);
[1, 2, 3, 10, 20, 30]

Moving on, if what you want is to add an element to the end of an array, then there is a simpler way than this one. It's to use the push() array method.

push()

The push() array method takes an argument and appends it to an array. Append simply means to add an item to the end of the array.

It's also possible to provide multiple arguments to push() at once. In this case, they are all added one after another to the end of the array.

Syntactically, push() could be shown as follows:

arr.push(item1[, item2 ... [, itemN]])

The best part of using push() to add a new item to an array is that we don't need to specify an index manually — the method takes care of all the insertion logic itself.

In the code below, we create an array nums and then add three numbers to it:

var nums = [1, 2, 3];

nums.push(10);
nums.push(20);
nums.push(30);

See how we don't have to care about giving indices of where to put the new elements at, when we use push().

Let's log each of the newly-added elements of nums and see what do we get.

nums[3]
10
nums[4]
20
nums[5]
30

As expected, we get the same numbers returned that were appended to nums.

Amazing!

Removing stuff

Often times while working with arrays, it's desired to remove items completely from it. This can be accomplished in a couple of ways depending on which element is wished to be removed.

pop()

To remove the last element from an array, we use the pop() array method.

It requires no argument and when called, removes and returns the last element from the given array.

Here is its syntax:

arr.pop()

Note that after calling pop(), the length of the array is changed and this is reflected in the length property.

Let's create an array nums and remove its last two elements one-by-one, meanwhile logging the removed element and the length of the array after calling the pop() method:

var nums = [1, 10, 50];

console.log('Removing:', nums.pop());
console.log('Length of nums:', nums.length);

console.log('\n');

console.log('Removing:', nums.pop());
console.log('Length of nums:', nums.length);
Removing: 50
Length of nums: 2

Removing: 10
Length of nums: 1

In the first log statement, nums.pop() removes 50 from nums and consequently returns it. This return value is displayed in the console, followed by the current length of the array (after executing the pop() method), which is now 2.

Similarly in the last set of log statements, nums.pop() removes 10 from nums and returns it. As before, this return value is displayed in the console, followed by the current length of the array (after executing the pop() method), which is now 1.

shift()

The shift() method is quite similar to pop() in that it also removes a single element from a given array and takes no argument.

The main difference is the position at which it operates — shift() removes the first element from an array and shifts all elements up by one position.

arr.shift()

Akin to pop(), it modifies the length of the given array.

Below we create an array nums and sequentially remove its first two elements, meanwhile logging the removed element and the new length of the array after the removal.

var nums = [1, 10, 50];

console.log('Removing:', nums.shift());
console.log('Length of nums:', nums.length);

console.log('\n');

console.log('Removing:', nums.shift());
console.log('Length of nums:', nums.length);
Removing: 1
Length of nums: 2

Removing: 10
Length of nums: 1

One thing to keep in mind is that it involves shifting of all subsequence items, after the first item, down by one position. This shifting takes time — internally the engine iterates over each element and repositions it one step backwards.

If you want to remove all elements from an array one-by-one, it's generally better to use pop() since it doesn't involve any shifting and is comparatively very efficient as compared to shift.

The Array() constructor

As with most of the data types in JavaScript, there is a global function which we could use to create an array. It is Array().

Now whether we call it in the context of a constructor i.e. with the new keyword, or in the context of a normal function, i.e. without new, it doesn't matter. What's returned in both cases is an array object.

Recall that the distinction between calling a function in the context of a constructor and in the context of a normal function exists only in primitive values. That is, the normal invocation returns a primitive while the constructor invocation returns an object.

For instance, Number() returns the primitive value 0, whereas new Number() returns a Number object wrapping the value 0.

The Array() constructor has two distinct forms.

When it is called with a single argument of type number, a sparse array is created with that many empty holes (we'll explore sparse arrays later on in this chapter):

new Array([length])

The length property of the array is set to the provided number argument. If the argument is a float, an error is thrown.

For instance, Array(5) won't create the array [5], but rather an array whose length is 5 and that is filled with empty slots. These empty slots don't have any value at all, not even undefined. They are totally blank!

We'll explore this type of invocation of Array() later on below.

On the otherhand, when Array() is called with multiple arguments, or with a single argument that is not a number, the arguments are taken to be elements of the array being created.

new Array([item1[, item2[, ...[, itemN]]]])

For instance, Array('2') would return the array ['2'], as the single argument provided to it is not a number. Similarly, Array(1, '2') would return the array [1, '2'], as it is called with multiple arguments (even though the first argument is a number).

There is nothing special about the return value of Array() — it's the same array that we'd get using the array literal syntax.

In the code below, we create an array using the Array() constructor and then process it:

var nums = new Array(1, 2, 3);

nums.push(10);
nums.push(20);

console.log(nums);
[1, 2, 3, 10, 20]

As you can see here, when an array is logged, it's displayed in the literal format, i.e. with the square brackets ([]).

Alright, let's come back to the special case of Array(), where there is only one argument specified.

Shown below is an actual demonstration:

var nums = Array(3);

console.log(nums);
[empty x 3]

Notice the log made. empty x 3 simply means that the array has 3 empty spaces in it.

The code above was executed in Google Chrome, and the log shown is from its console. Other browsers use other ways to show such arrays with holes in them.

Keep in mind these empty spaces are not undefined, even though when we access any one element, we get undefined returned as shown below:

var nums = Array(3);

console.log(nums[0]);
undefined

The reason why we get undefined returned when accessing these empty spaces is because the JavaScript interpreter is configured to always return undefined when accessing an undefined entity on a given value, such as when accessing a non-existent property on an object.

In this case, since the accessed index is empty, the interpreter returns undefined. The underlying value is not actually undefined.

JavaScript is undoubtedly very weird at times!

Now the question is that should you be using Array(n), where n is an integer to predefine the number of elements an array would have?

Well, there isn't any significant benefit at all of using Array(n) to preinitialise an array to a given length. Internally, JavaScript is completely different from how it looks on the front. What might seem like a valid optimisation may be just some redundant statement.

In short,

When working with arrays, it's always a good idea to keep things simple and use array literals only — no constructors.

But there is one case where using the Array() constructor could save you the time and space of creating an equivalent array using a loop. We'll see this case in the chapter JavaScript Array Methods.

Sparse arrays

Usually, the arrays we work with all the time in JavaScript have elements at contiguous positions. That is, there is no empty space before, after, or in between the elements; they come one after another.

Such arrays are commonly referred to as dense arrays.

Why 'dense'? Simply because all the elements are densely arranged one after another such that there is no empty space in between.

The opposite of dense arrays are sparse arrays.

Sparse arrays do not have elements at contiguous positions — they have empty holes in them.

In the previous sections, we showed two cases where the array obtained had holes in it.

Can you recall these two cases?

One is when we delete an array element using the delete keyword. The second is when we create an array by calling the Array() function with a single integer argument, like Array(3).

In both these cases, the array returned in the end has empty spaces in it. Elements are not jam-packed one after another — this is why we call these arrays sparse.

A sparse array's length would always be greater than the actual number of elements in it.

As an example consider the following code:

var nums = [1, 2, 3];
delete arr[1];

console.log(nums);
console.log(nums.length)
[1, empty, 3]
3

First we create an array nums with three elements and then delete the second element from it using delete. Now after this deletion, nums has actually 2 elements left, but its length property is still 3.

This is a characteristic common to all sparse arrays — they have less elements than the shown length.

Huge sparse arrays

JavaScript engines are highly optimized to make intelligent decisions based on what's happening or will eventually happen in a given program. In the case of sparse arrays, there are optimizations and decisions involved as well.

As we saw above, calling Array() with a single integer argument n creates a sparse array. When the integer n passed on to the function is quite large, which is figured out based on different heuristics, instead of creating an array of length n with as many empty fields, the engine creates an object with a property length set to n and returns that.

The array is not implemented as a true array in memory, but rather as an object where indices are merely keys. The object behaves similar to how a native JavaScript object, like {x: 10, y: 10}, would do.

This means that element-access expressions, such as arr[100] won't be as quick as with native arrays — they would be accessing the respective property on the array-like object, which would have to be searched for in the object and its prototypal chain.

In performance-intensive applications, this could cause memory problems as well as prolong the running times of element-access expressions.

Following from this behaviour, it's generally recommended to avoid using Array(n) to create large arrays, and also to avoid creating sparse arrays whenever possible.

Mutability

Back in the JavaScript String Basics chapter, we learnt that string in JavaScript are immutable in nature i.e once created they couldn't be changed.

Consider the code below where this idea could be seen:

var str = 'Good';
str[0] = 'F';

console.log(str)
Good

In the second statement, we aim to change the first character of the string str to 'F', and then log str to the console. The log is 'Good' instead of the expected value 'Food' — the string str wasn't changed. The assignment statement was just ignored.

The reason why the code above logs 'Good' instead of 'Food' is because, in JavaScript, strings are immutable and hence couldn't be modified once created. The only way to change the text within a string is to create a new string with the new piece of text.

In JavaScript, all string methods return a new string.

The opposite concept to this is that of mutability.

A value is said to be mutable if it could be changed in-place.

In JavaScript, arrays are mutable in nature. That is, we can change — or better to say, mutate — them once created.

The classic example to demonstrate the mutability of arrays is shown as follows:

var nums = [1, 2, 3];
nums[1] = 10;

console.log(nums);
[1, 10, 3]

We create an array nums with a couple of elements and then change its second element. After this, we log the array to see if the modification was considered or simply ignored (as in the case of strings).

The log made clearly shows that indeed the modification was considered and that the original array nums was changed. This confirms that, yes, arrays are mutable.

Dimensions

The dimension of an array is the number of times you have to specify an index to reach to a value

In all of the examples of arrays you have seen uptil now how many times did we need to specify an index? Once? Yes, absolutely and therefore all those arrays can be called single dimension arrays.

Recall the fact that array elements can be of any type in JavaScript - and arrays are no exception!

When we have arrays as array elements we have a multi-dimension array. More specifically the array can be two dimensional (2D), three dimensional (3D), and even further than that.

It's very rare to have 4D arrays. Just imagine yourself having to go through four indexes to reach to a final value! It'll be way too much messy and complicated!

Take a look at the code below:

var arr = [[1, 2], [5, 3]]; // a 2d array

The array arr has two elements - both arrays themselves.

If we want to access the number 1 in the code above, we need to first refer to the first element of arr, and then to the first element of this selected array.

The following snippet logs all the four numbers in the code above:

console.log(arr[0][0]); // 1
console.log(arr[0][1]); // 2
console.log(arr[1][0]); // 5
console.log(arr[0][1]); // 3

When we write arr[0][1], arr[0] returns the first element of arr, and since this element is itself an array, we can further fetch elements by using the same notation.

The part [1], after arr[0], fetches the second element out of the array arr[0] which is [1, 2], and consequently returns 2.

Such simple!

2D arrays are quite common in programming. 3d arrays are quite difficult to maintain, and hence appear only occasionally.

Checking for arrays

In JavaScript, it's possible to check whether a given value is an array, but at least not using the typeof operator. Can you recall why, back from the JavaScript Data Types chapter?

Well the answer is simple — because the operator returns 'object' even for arrays!

var arr = [1, 2];

console.log(typeof arr);
object

So how can we check for an array?

Well there are a couple of ways:

The instanceof operator

The simplest way to check whether a given value is an array or not is to use the instanceof operator.

Here's its syntax to check for an array:

value instanceof Array

If value is an array, this expression would return true; otherwise it would return false.

Let's try using instanceof on a couple of values:

[] instanceof Array
true
[1, 2, 3] instanceof Array
true
null instanceof Array
false
true instanceof Array
false
{0: 1, 1: 2} instanceof Array
false

In the first two cases, since the given value is an array, we get true returned. In all other cases, since the given values are not arrays, false is returned.

The best part about using instanceof is that it is very well supported an older browsers.

However, there is one case where instanceof doesn't work as expected. It's detailed below.

In an iframe, if you want to check whether an outsider value is an array, the following code won't work as expected:

// this is code inside an iframe
// outsideArray is obtained from the outside window

console.log(outsideArray instanceof Array);
false

The reason is because the outsider value is based on the Array object type in its own execution context, NOT on the Array object is another window's execution context. You'll learn more about this in the JavaScript Objects unit.

The instanceof operator

The idea behind the name instanceof comes from the fact that it checks whether an object is an instance of a given class. An instance is simply an object belonging to a given class. We'll learn more about instances and objects in the JavaScript Objects unit.

The Array.isArray() method

The second way to check whether a given value is an array is to use the isArray() method on the global Array object.

Syntactically, it could be shown as follows:

Array.isArray(value)

It returns true when the value argument is an array, and false otherwise.

Consider the following snippet:

Array.isArray([])
true
Array.isArray([1, 2, 3])
true
Array.isArray(null)
false
Array.isArray(true)
false
Array.isArray({0: 1, 1: 2})
false

The return value for each statement here is the same as that using the instanceof operator, as we saw above.

Unlike instanceof, Array.isArray() can also solve the outer-window array check problem we saw above.

// this is code inside an iframe
// outsideArray is obtained from the outside window

console.log(Array.isArray(outsideArray));
true