A JavaScript Array is a data structure that contains a group of elements. Typically these elements are all of the same data type, such as integer, string, or object. An example is an array with 7 string elements, each one representing a day of the week (‘monday‘, ‘tuesday‘, etc.).
Creating arrays in JavaScript is easy with the array literal notation. It consists of two square brackets that wrap optional array elements separated by a comma. Array elements can be any type, including number, string, boolean, null, undefined, object, function, regular expression and other arrays. Here are some examples:
// empty array with no elements var empty = []; // array with 2 string elements var days = ["Sunday", "Monday"]; // array with elements of different types var mixed = [false, 2.70, "Hello"]; // elements can be arbitrary expressions var suffix = "tree"; var trees = ["Oak" + suffix, "Elm" + suffix]; // 2 dimensional array with object literals var array2 = [[1,{x:1, y:2}], [2, {x:3, y:4}]]; // the 3rd element is undefined var colors = ["Red", "Green", undefined]; // no value in the 1st element, it is undefined var hobbies = [,"Art"];
JavaScript arrays are a special type of object. To access an array item, the [] operator is
used, for example colors[2]
. The [] operator converts the expression inside
the square brackets to a string. For instance, if it is a numeric value, JavaScript converts
it to a string and then uses that string as the property name, similar to the square
bracket notation of objects to access their properties. This explains why JavaScript
arrays are not nearly as fast as arrays in other languages.
In the next example, 0, 1, and 2 are converted to "0", "1", and "2" respectively:
var days = ["Sunday", "Monday"]; // an array with 2 elements var firstDay = days[0]; // index 0 is converted to "0" days[1] = "Ford"; days[2] = "Tuesday"; // writes element 3
Since an array is a subclass of object, there is nothing to stop you from adding properties on an array instance:
var cars = ["Toyota"];
cars["tires"] = 4;
console.log(cars["tires"]); // => 4
Before reviewing iterating over an array we need to understand how to determine how many
elements there are in an array. The closest we can get is JavaScript's length
property.
JavaScript automatically keeps track of the length property that exists on each array. Unlike other languages, the length of a JavaScript array does not hold the array's upper bound.
var days = ["Sunday", "Monday"];
console.log(days.length); // => 2
var cars = [];
cars[1] = "Honda";
cars[3] = "Fiat";
console.log(cars.length); // => 4
In this example, the length
property of the cars array is greater than the number
of elements. The value length
is 4, but the actual number of array elements is
just 2. So, how does this work? The rule with length
is that it is the
last numerical index plus one. An array with gaps in its elements is called a sparse array.
Here is another unusual case: an element with the key six
is added to the above
script, but is ignored by the length
property.
var cars = [];
cars[1] = "Honda";
cars[3] = "Fiat";
cars["six"] = "Volkswagen";
console.log(cars.length); // => still returns 4
These examples also demonstrate that JavaScript arrays are dynamically sized, meaning that you will never see array out-of-bound errors.
With an irregular array how do we iterate over all its elements? Let's first consider using for loops.
var cars = [];
cars[1] = "Ford";
cars[3] = "BMW";
cars["six"] = "Honda";
for (var i = 0; i < cars.length; i++) {
console.log(cars[i]); // => undefined, Ford, undefined, BMW
}
// To skip missing and undefined elements, add this condition.
for (var i = 0; i < cars.length; i++) {
if (cars[i] === undefined) // skip undefined elements
continue;
console.log(cars[i]); // => Ford, BMW
}
Notice that the array element with key six
won't appear when iterating using a for-loop.
To see all index values we need to use a for-in loop, like so:
var cars = [];
cars[1] = "Ford";
cars[3] = "BMW";
cars["six"] = "Honda";
for (var index in cars) {
console.log(cars[index]); // Ford, BMW, Honda
}
There are a couple of things to be aware of when using a for-in loop on arrays. First, if you are expecting the elements of your array to appear in numerical order, don't trust the for-in loop. They can appear in any order. Secondly, the for-in loop not only returns all array properties (elements), but it also returns the properties that are inherited through the prototype chain.
With both the length property and the for-in loop being somewhat peculiar, it is best to always use numeric index values starting with 0 going up. In that case arrays will behave similar to most other languages and the length property as well as the for-loop will work as you would expect. Here is an example:
var cars = [];
cars[0] = "Ford";
cars[1] = "BMW";
cars[2] = "Honda";
console.log(cars.length); // => 3
for (var i = 0, len = cars.length; i < len; i++) {
console.log(cars[i]); // => Ford, BMW, Honda
}
JavaScript does not natively support arrays of more than one dimension; you have to create them yourselves. Fortunately, they are easy to model with arrays of arrays. In the next example we create a 5 by 5 identity matrix with a value of 1 on diagonal elements and 0 everywhere else.
var twoDim = [];
for (var row = 0; row < 5; row++) {
var oneDim = [];
for (var col = 0; col < 5; col++) {
oneDim[col] = (row === col) ? 1 : 0; // 0 or 1 (diag)
}
twoDim[row] = oneDim;
}
console.log(twoDim[4][2]); // => 0
console.log(twoDim[3][3]); // => 1
The last two statements also demonstrate how you would access elements in a 2-dimensional array: simply use the [] operator twice, the first for the row and second for the column index.
To remove an element from an array you can use the delete operator. Deleting an element does not affect the length property and the array becomes sparse. Also, elements with higher indexes to the right of the deleted element do not get shifted down to fill in the gap.
var days = ["Sunday", "Monday", "Tuesday", "Wednesday"];
delete days[2]; // => delete the element at index 2
console.log(days[2]); // => undefined
console.log(days.length); // => still 4
An alternative to delete is the built-in array method splice()
. The difference
with the delete operator is that splice()
does not make the array sparse and
shifts the elements to higher or lower positions as necessary making sure that no gap is left.
This is discussed in the next section.
The built-in array method splice()
is rather versatile and inserts new,
deletes existing, and replaces existing elements with new elements in the array.
The beauty of splice()
is that it does not leave the array sparse because
it will shift the elements to higher or lower positions as necessary; there will be no gap left.
The first argument of splice()
specifies the ordinal position at which the operation
is to start. The required second argument specifies the number of elements to delete. The method
operates on the array at hand and the return value consists of the array elements deleted.
In the following example the first splice
indicates to begin the operation at
index 5 and length 2. When the operation completes, the elements f
and g
have been removed. The second splice indicates a starting position of 2 and length 1.
After the operation completes, the letters array has these elements left: a, b, e
.
var letters = ["a", "b", "c", "d", "e", "f", "g"];
console.log(letters.splice(5, 2)); // => f, g (deleted elements)
console.log(letters); // => a, b, c, d, e
console.log(letters.splice(2, 1)); // => c (the deleted element)
console.log(letters); // => a, b, d, e
Using the third argument and higher, the splice method can also be used to replace
one or more elements with others. In the example below, the splice starts at position
1 and deletes two elements. Next it replaces the gap with the three elements provided:
e, f, g
. The final array has 5 elements.
var letters = ["a", "b", "c", "d"];
console.log(letters.splice(1, 2, "e", "f", "g")); // => b, c (deleted ones)
console.log(letters); // => a, e, f, g, d
You can also use splice()
to inject new elements into an array without
deleting existing ones. Simply specify a 0 for the second argument, like so:
var letters = ["a", "b", "e"];
console.log(letters.splice(2, 0, "c", "d")); // => no elements returned
console.log(letters); // => a,b,c,d,e
The built-in push()
array method appends one or more elements to the
end of an array, increments the length as appropriate, and returns the length of the
modified array. The built-in pop()
array method does the opposite;
it deletes the last element, reduces the length by one, and returns the deleted element.
var days = ["Monday"];
console.log(days.push("Tuesday", "Wednesday")); // => 3
console.log(days); // => Monday, Tuesday, Wednesday
console.log(days.pop()); // => Wednesday
console.log(days); // => Monday, Tuesday
console.log(days.push("Wednesday")); // => 3
console.log(days); // => Monday, Tuesday, Wednesday
This code shows that the array methods push()
and pop()
make it very
easy to build a LIFO (last in, first out) stack with JavaScript arrays.
The built-in array method unshift()
inserts one or more elements to the beginning of an
array, shifts the existing elements up to higher indexes to make space for the new elements,
increments the length by the number of elements inserted, and returns the new length.
The shift()
removes the first element, shifts all the elements with higher
indexes down one position to fill the gap, reduces the length by one, and returns
value of the element that it removed.
var days = ["Wednesday"];
console.log(days.unshift("Monday", "Tuesday")); // => 3
console.log(days) // => Monday, Tuesday, Wednesday
console.log(days.shift()); // => Monday
console.log(days.shift()); // => Tuesday
console.log(days); // => Wednesday
As you can see, the unshift()
and shift()
methods make it very easy
to build a FIFO (first in, first out) queue with JavaScript arrays.
The array method map()
is used to change each element in the array and return a new
array with the modified elements. For example, you could use map to iterate over an array
with numbers and then create a new array in which each element is double the value of the
original array. The array returned is of the same length as the original array.
Here's how map()
works. Each element of the array on which map()
is invoked is passed to a callback function you pass into to map()
as an argument.
The values returned by the callback are returned in a new array, which is called double
in our example below. The original array with the name values
on which map()
is invoked, is not modified. Since an array is an object, you can also add function methods
directly in an array itself.
var values = [1, 2, 3, 4, 5];
console.log(values.length); // => 5
var double = values.map(function (value) {
return 2 * value;
});
console.log(double); // => 2, 4, 6, 8, 10
console.log(double.length); // => 5
As you can see, both arrays have the same length. If the values array were a sparse array with missing values, then the double array would also be sparse with gaps in the elements. Both would still be of the same length.
Array method reduce()
is an accumulator method that iterates over the elements
of the array, from left to right, and reduce it to a single value.
The first argument in reduce()
is the callback function and the second optional
argument is the initial value. The callback function accepts 4 arguments, of which the
first 2 are the previous value and the current value. This is best explained with an example.
Say we have a numeric array and we wish to get the sum of all elements.
This is how you would do it with reduce()
:
var values = [2, 4, 6];
var sum = values.reduce(function (prev, curr) {
return prev + curr;
},
0 // initial value
);
console.log(sum); // => 12
When the first callback call is made, the arguments passed to the callback function are 0
(the initial value and second argument to reduce()
and 2 (the first element of the array).
The function returns 0 + 2 = 2. In the second call, the 2 and 4 are passed and 2 + 4 = 6 is
returned. In the last call, 6 and 6 are passed and the final result 12 is returned.
The second argument to reduce()
is optional. Had you omitted the 0 in the example
above, the first and second elements of the array would have been passed to the function,
which also works.