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 a head start on using arrays in JavaScript.
Arrays 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 represent an extremely elementary concept in JavaScript and programming, in general, yet they're one of the most paramount tools in a programmer's skills inventory. Knowing them will allow us to work more effectively in our programs and be able to implement 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 over to explore the array of concepts related to arrays.
At the core level, from an abstract perspective:
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.
Some people also define an array as an ordered collection of items. This is simply just 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 the sequence. 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.
Alright, so with this basic theory in mind, let's now see how to create an array in JavaScript.
Creating arrays using 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 now consider an example.
In the following code, we create an array holding the three numbers 1, 2, and 3:
var nums = [1, 2, 3];
Easy, wasn't this?
Create an array holding the three strings 'Hi'
, 'Hello'
, and 'Bye'
.
Very simple:
var strs = ['Hi', 'Hello', 'Bye'];
An array is treated specially when logged in the console — its output is made interactive.
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 indexes, 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:
Coming back to the array nums
, let's now compute the sum of its first 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. (Once again, this is something you've already seen back from the JavaScript Strings — Basics chapter.)
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 we wish to access.
Alright, now that we know how to access an array element, it's time to sum the first two numbers of nums
. This is accomplished as follows:
var nums = [1, 2, 3];
console.log(nums[0] + nums[1]);
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
.
Going forward, what if we decide to change the second number in nums
from 2
to 20
during the course of the program?
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 we wish to access, and newValue
is the new value we want at arr[index]
.
Let's see a quick example:
var nums = [1, 2, 3];
nums[1] = 20;
console.log(nums[1]);
As can be seen in the 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
.
Let's add this new number to the third number in nums
and see the return value:
nums[1] + nums[2]
23
As expected, it's 23
(20
+ 3
).
So at this point, we know how to create an array, put stuff in it while defining it, and then later on change that stuff. This has set the stage for exploring more features of arrays in JavaScript.
For the next step, let's find out how to add more elements to an array and remove given elements from an array.
Adding elements to an array
There is more than just one way to add an element to an array in JavaScript. In this section, we'll cover the two most common ways — using bracket notation and the push()
method.
The rest of the ways are usually more specific to given applications. We'll discuss them in the next chapter, JavaScript Arrays — Array Methods.
Bracket notation
Many programming languages won't allow us to access an 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 — array indexes 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 the context of access, 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 to it. Each item is added by assigning a value to the index nums.length
, which comes right after the last element in the array.
Note that since arr.length
merely returns an integer, if we already know the last index of the array beforehand, we can even manually provide the value for the next index.
In the code below, we do the same thing as we did above except for that now we manually pass the new indexes:
var nums = [1, 2, 3];
nums[3] = 10;
nums[4] = 20;
nums[5] = 30;
console.log(nums);
[1, 2, 3, 10, 20, 30]
Good.
Even though these approaches are absolutely fine for adding an element to the end of an array, as you'll agree, they are a little bit too much for such a simple task.
A much simpler way to add an element to the end of an array, and one which is the typical choice for this use case, is to use the push()
array method.
push()
The push()
array method takes a value as 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 the insertion logic itself.
In the code below, we create an array nums
and then add three numbers to it, just as before:
var nums = [1, 2, 3];
nums.push(10);
nums.push(20);
nums.push(30);
console.log(nums);
[1, 2, 3, 10, 20, 30]
See how we don't have to care about giving indexes of where to put the new elements when we use push()
.
Amazing!
Removing elements from an array
Often times while working with arrays, it's desired to remove given elements. 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.
Note that after calling pop()
, the length of the array is changed and this is reflected in the length
property of the array.
Let's remove the last two elements from our nums
array, meanwhile logging the removed elements and the length
of the array upon each removal:
var nums = [1, 2, 3];
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);
Length of nums: 2
Removing: 2
Length of nums: 1
In the first set of log statements, nums.pop()
removes 3
(the last element) from nums
and consequently returns it. This return value is output in the console, followed by the new length of the array (which is now 2
).
Similarly in the second set of log statements, nums.pop()
removes 2
from nums
and returns it. As before, this return value is displayed in the console, followed by the current length of the array (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 lies in the position at which it operates — shift()
removes the first element from an array and shifts all elements up by one position.
Akin to pop()
, shift()
also modifies the length of the given array.
As before, let's witness shift()
removing a couple of elements from our nums
array, logging the removed value and the new length of the array:
var nums = [1, 2, 3];
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);
Length of nums: 2
Removing: 2
Length of nums: 1
One important thing to keep in mind regarding shift()
is that it involves shifting all subsequent items after the first item down by one position.
Compared to removing an element from the end of an array (using push()
), this shifting takes some time — internally the engine iterates over each element and repositions it one step backwards.
So, if all what we want to do is remove all the elements from an array one-by-one without any specific place to begin (the start or the end), it's clearly better to use pop()
since it doesn't involve any shifting costs.
The following code removes all elements from nums
using a while
loop, logging each removed element:
var nums = [1, 2, 3];
while (nums.length) {
console.log('Removing:', nums.pop());
}
console.log(nums);
The condition nums.length
in the header of while
means that the loop only continues so long as nums.length
returns a truthy value, i.e. the array is non-empty.
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 — 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.
However, we'll use the constructor variant to stress emphasis on the fact that we're working with an object.
Array()
vs. new Array()
doesn't matter
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
.
In the case of Array()
, since arrays are objects already, it doesn't make any difference whether we call Array()
or new Array()
.
The Array()
constructor has two distinct forms.
Calling Array()
with a single integer argument
When it is called with a single integer argument, Array()
creates a sparse array 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 length
argument.
For instance, new Array(5)
would NOT create the array [5]
but rather an array whose length is 5, comprised of 5 empty slots.
These empty slots don't have any value at all, not even undefined
; they are totally blank. We'll discover what this means very shortly below.
Shown below is a demonstration:
var nums = new Array(3);
console.log(nums);
Notice the log made: empty x 3
simply means that the array has 3 empty spaces in it.
Keep in mind that these empty spaces are NOT undefined
, even though when we access any one element, we get undefined
returned as shown below:
nums[0]
The reason why we get undefined
returned when accessing these empty spaces is because of the very nature of JavaScript — it's configured to always return undefined
when accessing an undefined entity on a given value, for e.g. when accessing a non-existent property on an object.
In this case, since the accessed index is empty, and technically undefined, the interpreter returns undefined
. This does NOT at all mean that the underlying value is actually undefined
.
JavaScript is undoubtedly very weird at times!
So what exactly is the underlying value? Well, we'll answer this in the next section, where we go in depth over sparse arrays in JavaScript, after the following discussion.
Anyways, now a fair question that comes in the mind of an experienced developer is that: Should we be using new Array(n)
, where n
is an integer, to pre-initialize an array?
For example, if we know that an array ought to have 10 elements in it, is it better to pre-initialize it using new Array(10)
and then work with it, or just stick to the normal approach of beginning with an empty array and then, perhaps, adding the 10 elements using push()
?
What does your gut feeling say?
Should we pre-initialize arrays using new Array(n)
?
Well, there isn't any significant benefit at all of using new Array(n)
to pre-initialize an array to a given length.
Being heavily optimized by modern-day engines, internally, JavaScript often functions quite differently from how it looks on the front. What seems like a valid optimization may just be some redundant statement to have or, worse yet, a performance degrader.
There is absolutely NO need of pre-initializing an array to a given length using new Array(n)
with the thinking that it'll speeden up the performance of the app.
But obviously, if we want an array to be pre-filled with given values before we go on and process it, there's no reason to not use new Array(n)
. For instance, if we want an array pre-filled with a hundred 0
s before beginning a computation routine, we can well leverage this approach and save us a handful of lines of code in manually doing so using a for
loop.
We'll see an example of this in the next chapter, JavaScript Arrays — Array Methods, once we get to the fill()
method.
Calling Array()
with multiple arguments
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, new Array('2')
would give the array ['2']
, as the single argument provided to it is not a number. Similarly, new Array(1, '2')
would give 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 new Array()
in this case — it's the exact same array that we'd get using the array literal syntax.
In the code below, we re-create the nums
array using the Array()
constructor, and then exercise what we learnt in this chapter regarding adding elements to an array:
var nums = new Array(1, 2, 3);
nums.push(10);
nums.push(20);
console.log(nums);
The expression new Array(1, 2, 3)
is effectively the same as [1, 2, 3]
.
Mutability of arrays
Back in the JavaScript Strings — Basics chapter, we learnt that strings in JavaScript are immutable in nature, i.e. once created, they couldn't be changed.
Consider the code below where this idea is reviewed:
var str = 'Good';
str[0] = 'F';
console.log(str)
In the second statement, we aim to change the first character of the string str
from G
to F
. The console's output, however, shows that this wasn't entertained — we get 'Good'
instead of the expected value 'Food'
.
The reason of this behavior in JavaScript is because strings are immutable and hence couldn't be modified once created. The only way to change a character within a string is to create a new string with the new character.
The opposite concept to this is that of mutability.
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 mutability of arrays is as follows:
var nums = [1, 2, 3];
nums[1] = 10;
console.log(nums);
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).
[1, 10, 3]
The console's output is evident of the fact that the array nums
was mutated. This confirms that arrays are mutable in JavaScript.
Array dimensions
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.
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!
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);
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
[1, 2, 3] instanceof Array
null instanceof Array
true instanceof Array
{0: 1, 1: 2} instanceof Array
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);
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([])
Array.isArray([1, 2, 3])
Array.isArray(null)
Array.isArray(true)
Array.isArray({0: 1, 1: 2})
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));