An array is a map from indices (natural numbers, starting at zero) to arbitrary values. The values (the range of the map) are called the array’s elements. The most convenient way of creating an array is via an array literal. Such a literal enumerates the array elements; an element’s position implicitly specifies its index.
In this chapter, I will first cover basic array mechanisms, such as indexed access and the length
property, and then go over array methods.
This section provides a quick overview of arrays. Details are explained later.
As a first example, we create an array arr
via an array literal (see Creating Arrays) and access elements (see Array Indices):
> var arr = [ 'a', 'b', 'c' ]; // array literal > arr[0] // get element 0 'a' > arr[0] = 'x'; // set element 0 > arr [ 'x', 'b', 'c' ]
We can use the array property length
(see length) to remove and append elements:
> var arr = [ 'a', 'b', 'c' ]; > arr.length 3 > arr.length = 2; // remove an element > arr [ 'a', 'b' ] > arr[arr.length] = 'd'; // append an element > arr [ 'a', 'b', 'd' ]
The array method push()
provides another way of appending an element:
> var arr = [ 'a', 'b' ]; > arr.push('d') 3 > arr [ 'a', 'b', 'd' ]
The ECMAScript standard specifies arrays as maps (dictionaries) from indices to values. In other words, arrays may not be contiguous and can have holes in them. For example:
> var arr = []; > arr[0] = 'a'; 'a' > arr[2] = 'b'; 'b' > arr [ 'a', , 'b' ]
The preceding array has a hole: there is no element at index 1. Holes in Arrays explains holes in more detail.
Note that most JavaScript engines optimize arrays without holes internally and store them contiguously.
Arrays are still objects and can have object properties. Those are not considered part of the actual array; that is, they are not considered array elements:
> var arr = [ 'a', 'b' ]; > arr.foo = 123; > arr [ 'a', 'b' ] > arr.foo 123
You create an array via an array literal:
var
myArray
=
[
'a'
,
'b'
,
'c'
];
Trailing commas in arrays are ignored:
> [ 'a', 'b' ].length 2 > [ 'a', 'b', ].length 2 > [ 'a', 'b', ,].length // hole + trailing comma 3
There are two ways to use the constructor Array
: you can create an empty array with a given length or an array whose elements are the given values. For this constructor, new
is optional: invoking it as a normal function (without new
) does the same as invoking it as a constructor.
> var arr = new Array(2); > arr.length 2 > arr // two holes plus trailing comma (ignored!) [ , ,]
Some engines may preallocate contiguous memory when you call Array()
in this manner, which may slightly improve performance. However, be sure that the increased verbosity and redundancy is worth it!
This way of invoking Array
is similar to an array literal:
// The same as ['a', 'b', 'c']:
var
arr1
=
new
Array
(
'a'
,
'b'
,
'c'
);
The problem is that you can’t create arrays with a single number in them, because that is interpreted as creating an array whose length
is the number:
> new Array(2) // alas, not [ 2 ] [ , ,] > new Array(5.7) // alas, not [ 5.7 ] RangeError: Invalid array length > new Array('abc') // ok [ 'abc' ]
If you need multiple dimensions for elements, you must nest arrays. When you create such nested arrays, the innermost arrays can grow as needed. But if you want direct access to elements, you need to at least create the outer arrays. In the following example, I create a three-by-three matrix for Tic-tac-toe. The matrix is completely filled with data (as opposed to letting rows grow as needed):
// Create the Tic-tac-toe board
var
rows
=
[];
for
(
var
rowCount
=
0
;
rowCount
<
3
;
rowCount
++
)
{
rows
[
rowCount
]
=
[];
for
(
var
colCount
=
0
;
colCount
<
3
;
colCount
++
)
{
rows
[
rowCount
][
colCount
]
=
'.'
;
}
}
// Set an X in the upper right corner
rows
[
0
][
2
]
=
'X'
;
// [row][column]
// Print the board
rows
.
forEach
(
function
(
row
)
{
console
.
log
(
row
.
join
(
' '
));
});
Here is the output:
. . X . . . . . .
I wanted the example to demonstrate the general case. Obviously, if a matrix is so small and has fixed dimensions, you can set it up via an array literal:
var
rows
=
[
[
'.'
,
'.'
,
'.'
],
[
'.'
,
'.'
,
'.'
],
[
'.'
,
'.'
,
'.'
]
];
When you are working with array indices, you must keep in mind the following limits:
Indices that are out of range are treated as normal property keys (strings!). They don’t show up as array elements and they don’t influence the property length
. For example:
> var arr = []; > arr[-1] = 'a'; > arr [] > arr['-1'] 'a' > arr[4294967296] = 'b'; > arr [] > arr['4294967296'] 'b'
The in
operator detects whether an object has a property with a given key. But it can also be used to determine whether a given element index exists in an array. For example:
> var arr = [ 'a', , 'b' ]; > 0 in arr true > 1 in arr false > 10 in arr false
In addition to deleting properties, the delete
operator also deletes array elements. Deleting elements creates holes (the length
property is not updated):
> var arr = [ 'a', 'b' ]; > arr.length 2 > delete arr[1] // does not update length true > arr [ 'a', ] > arr.length 2
You can also delete trailing array elements by decreasing an array’s length (see length for details). To remove elements without creating holes (i.e., the indices of subsequent elements are decremented), you use Array.prototype.splice()
(see Adding and Removing Elements (Destructive)). In this example, we remove two elements at index 1:
> var arr = ['a', 'b', 'c', 'd']; > arr.splice(1, 2) // returns what has been removed [ 'b', 'c' ] > arr [ 'a', 'd' ]
This is an advanced section. You normally don’t need to know the details explained here.
Array indices are not what they seem. Until now, I have pretended that array indices are numbers. And that is how JavaScript engines implement arrays, internally. However, the ECMAScript specification sees indices differently. Paraphrasing Section 15.4:
P
(a string) is an array index if and only if ToString
(ToUint32(P))
is equal to P
and ToUint32(P)
is not equal to 232−1. What this means is explained momentarily.
In other words, in the world of the spec all values in brackets are converted to strings and interpreted as property keys, even numbers. The following interaction demonstrates this:
> var arr = ['a', 'b']; > arr['0'] 'a' > arr[0] 'a'
To be an array index, a property key P
(a string!) must be equal to the result of the following computation:
P
to a number.
That means that an array index must be a stringified integer i in the 32-bit range 0 ≤ i < 232−1. The upper limit has been explicitly excluded in the spec (as quoted previously). It is reserved for the maximum length. To see how this definition works, let’s use the function ToUint32()
from 32-bit Integers via Bitwise Operators.
First, a string that doesn’t contain a number is always converted to 0, which, after stringification, is not equal to the string:
> ToUint32('xyz') 0 > ToUint32('?@#!') 0
Second, a stringified integer that is out of range is also converted to a completely different integer, which is not equal to the string, after stringification:
> ToUint32('-1') 4294967295 > Math.pow(2, 32) 4294967296 > ToUint32('4294967296') 0
Third, stringified noninteger numbers are converted to integers, which are, again, different:
> ToUint32('1.371') 1
Note that the specification also enforces that array indices don’t have exponents:
> ToUint32('1e3') 1000
And that they don’t have leading zeros:
> var arr = ['a', 'b']; > arr['0'] // array index 'a' > arr['00'] // normal property undefined
The basic function of the length
property is to track the highest index in an array:
> [ 'a', 'b' ].length 2 > [ 'a', , 'b' ].length 3
Thus, length
does not count the number of elements, so you’d have to write your own function for doing so. For example:
function
countElements
(
arr
)
{
var
elemCount
=
0
;
arr
.
forEach
(
function
()
{
elemCount
++
;
});
return
elemCount
;
}
To count elements (nonholes), we have used the fact that forEach
skips holes.
Here is the interaction:
> countElements([ 'a', 'b' ]) 2 > countElements([ 'a', , 'b' ]) 2
Manually increasing the length of an array has remarkably little effect on an array; it only creates holes:
> var arr = [ 'a', 'b' ]; > arr.length = 3; > arr // one hole at the end [ 'a', 'b', ,]
The last result has two commas at the end, because a trailing comma is optional and thus always ignored.
What we just did did not add any elements:
> countElements(arr) 2
However, the length
property does act as a pointer indicating where to insert new elements. For example:
> arr.push('c') 4 > arr [ 'a', 'b', , 'c' ]
Thus, setting the initial length of an array via the Array
constructor creates an array that is completely empty:
> var arr = new Array(2); > arr.length 2 > countElements(arr) 0
If you decrease the length of an array, all elements at the new length and above are deleted:
> var arr = [ 'a', 'b', 'c' ]; > 1 in arr true > arr[1] 'b' > arr.length = 1; > arr [ 'a' ] > 1 in arr false > arr[1] undefined
If you set an array’s length to 0, then it becomes empty. That allows you to clear an array for someone else. For example:
function
clearArray
(
arr
)
{
arr
.
length
=
0
;
}
Here’s the interaction:
> var arr = [ 'a', 'b', 'c' ]; > clearArray(arr) > arr []
Note, however, that this approach can be slow, because each array element is explicitly deleted. Ironically, creating a new empty array is often faster:
arr
=
[];
You need to be aware of the fact that setting an array’s length to zero affects everybody who shares the array:
>
var
a1
=
[
1
,
2
,
3
];
>
var
a2
=
a1
;
>
a1
.
length
=
0
;
>
a1
[]
>
a2
[]
In contrast, assigning an empty array doesn’t:
>
var
a1
=
[
1
,
2
,
3
];
>
var
a2
=
a1
;
>
a1
=
[];
>
a1
[]
>
a2
[
1
,
2
,
3
]
The maximum array length is 232−1:
> var arr1 = new Array(Math.pow(2, 32)); // not ok RangeError: Invalid array length > var arr2 = new Array(Math.pow(2, 32)-1); // ok > arr2.push('x'); RangeError: Invalid array length
Arrays are maps from indices to values. That means that arrays can have holes, indices smaller than the length that are missing in the array. Reading an element at one of those indices returns undefined
.
It is recommended that you avoid holes in arrays. JavaScript handles them inconsistently (i.e., some methods ignore them, other don’t). Thankfully, you normally don’t need to know how holes are handled: they are rarely useful and affect performance negatively.
You can create holes by assigning to array indices:
> var arr = []; > arr[0] = 'a'; > arr[2] = 'c'; > 1 in arr // hole at index 1 false
You can also create holes by omitting values in array literals:
> var arr = ['a',,'c']; > 1 in arr // hole at index 1 false
You need two trailing commas to create a trailing hole, because the last comma is always ignored:
> [ 'a', ].length 1 > [ 'a', ,].length 2
This section examines the differences between a hole and undefined
as an element. Given that reading a hole returns undefined
, both are very similar.
An array with holes is called sparse. An array without holes is called dense. Dense arrays are contiguous and have an element at each index—starting at zero, and ending at length
− 1. Let’s compare the following two arrays, a sparse array and a dense array. The two are very similar:
var
sparse
=
[
,
,
'c'
];
var
dense
=
[
undefined
,
undefined
,
'c'
];
A hole is almost like having the element undefined
at the same index. Both arrays have the same length:
> sparse.length 3 > dense.length 3
But the sparse array does not have an element at index 0:
> 0 in sparse false > 0 in dense true
Iteration via for
is the same for both arrays:
> for (var i=0; i<sparse.length; i++) console.log(sparse[i]); undefined undefined c > for (var i=0; i<dense.length; i++) console.log(dense[i]); undefined undefined c
Iteration via forEach
skips the holes, but not the undefined elements:
> sparse.forEach(function (x) { console.log(x) }); c > dense.forEach(function (x) { console.log(x) }); undefined undefined c
Some operations involving arrays ignore holes, while others consider them. This sections explains the details.
forEach()
skips holes:
> ['a',, 'b'].forEach(function (x,i) { console.log(i+'.'+x) }) 0.a 2.b
every()
also skips holes (similarly: some()
):
> ['a',, 'b'].every(function (x) { return typeof x === 'string' }) true
map()
skips, but preserves holes:
> ['a',, 'b'].map(function (x,i) { return i+'.'+x }) [ '0.a', , '2.b' ]
filter()
eliminates holes:
> ['a',, 'b'].filter(function (x) { return true }) [ 'a', 'b' ]
join()
converts holes, undefined
s, and null
s to empty strings:
> ['a',, 'b'].join('-') 'a--b' > [ 'a', undefined, 'b' ].join('-') 'a--b'
sort()
preserves holes while sorting:
> ['a',, 'b'].sort() // length of result is 3 [ 'a', 'b', , ]
The for-in
loop correctly lists property keys (which are a superset of array indices):
> for (var key in ['a',, 'b']) { console.log(key) } 0 2
apply()
turns each hole into an argument whose value is undefined
. The following interaction demonstrates this: function f()
returns its arguments as an array. When we pass apply()
an array with three holes in order to invoke f()
, the latter receives three undefined
arguments:
> function f() { return [].slice.call(arguments) } > f.apply(null, [ , , ,]) [ undefined, undefined, undefined ]
That means that we can use apply()
to create an array with undefined
s:
> Array.apply(null, Array(3)) [ undefined, undefined, undefined ]
apply()
translates holes to undefined
s in empty arrays, but it can’t be used to plug holes in arbitrary arrays (which may or may not contain holes). Take, for example, the arbitrary array [2]
:
> Array.apply(null, [2]) [ , ,]
The array does not contain any holes, so apply()
should return the same array. Instead, it returns an empty array with length 2 (all it contains are two holes). That is because Array()
interprets single numbers as array lengths, not as array elements.
As we have seen, filter()
removes holes:
> ['a',, 'b'].filter(function (x) { return true }) [ 'a', 'b' ]
Use a custom function to convert holes to undefined
s in arbitrary arrays:
function
convertHolesToUndefineds
(
arr
)
{
var
result
=
[];
for
(
var
i
=
0
;
i
<
arr
.
length
;
i
++
)
{
result
[
i
]
=
arr
[
i
];
}
return
result
;
}
Using the function:
> convertHolesToUndefineds(['a',, 'b']) [ 'a', undefined, 'b' ]
Array.isArray(obj)
true
if obj
is an array. It correctly handles objects that cross realms (windows or frames)—as opposed to instanceof
(see Pitfall: crossing realms (frames or windows)).
In the following sections, array prototype methods are grouped by functionality. For each of the subsections, I mention whether the methods are destructive (they change the arrays that they are invoked on) or nondestructive (they don’t modify their receivers; such methods often return new arrays).
All of the methods in this section are destructive:
Array.prototype.shift()
Removes the element at index 0 and returns it. The indices of subsequent elements are decremented by 1:
> var arr = [ 'a', 'b' ]; > arr.shift() 'a' > arr [ 'b' ]
Array.prototype.unshift(elem1?, elem2?, ...)
Prepends the given elements to the array. It returns the new length:
> var arr = [ 'c', 'd' ]; > arr.unshift('a', 'b') 4 > arr [ 'a', 'b', 'c', 'd' ]
Array.prototype.pop()
Removes the last element of the array and returns it:
> var arr = [ 'a', 'b' ]; > arr.pop() 'b' > arr [ 'a' ]
Array.prototype.push(elem1?, elem2?, ...)
Adds the given elements to the end of the array. It returns the new length:
> var arr = [ 'a', 'b' ]; > arr.push('c', 'd') 4 > arr [ 'a', 'b', 'c', 'd' ]
apply()
(see Function.prototype.apply(thisValue, argArray)) enables you to destructively append an array arr2
to another array arr1
:
> var arr1 = [ 'a', 'b' ]; > var arr2 = [ 'c', 'd' ]; > Array.prototype.push.apply(arr1, arr2) 4 > arr1 [ 'a', 'b', 'c', 'd' ]
Array.prototype.splice(start, deleteCount?, elem1?, elem2?, ...)
> var arr = [ 'a', 'b', 'c', 'd' ]; > arr.splice(1, 2, 'X'); [ 'b', 'c' ] > arr [ 'a', 'X', 'd' ]
Special parameter values:
start
can be negative, in which case it is added to the length to determine the start index. Thus, -1
refers the last element, and so on.
deleteCount
is optional. If it is omitted (along with all subsequent arguments), then all elements at and after index start
are removed.
In this example, we remove all elements after and including the second-to-last index:
> var arr = [ 'a', 'b', 'c', 'd' ]; > arr.splice(-2) [ 'c', 'd' ] > arr [ 'a', 'b' ]
These methods are also destructive:
Array.prototype.reverse()
Reverses the order of the elements in the array and returns a reference to the original (modified) array:
> var arr = [ 'a', 'b', 'c' ]; > arr.reverse() [ 'c', 'b', 'a' ] > arr // reversing happened in place [ 'c', 'b', 'a' ]
Array.prototype.sort(compareFunction?)
Sorts the array and returns it:
> var arr = ['banana', 'apple', 'pear', 'orange']; > arr.sort() [ 'apple', 'banana', 'orange', 'pear' ] > arr // sorting happened in place [ 'apple', 'banana', 'orange', 'pear' ]
Keep in mind that sorting compares values by converting them to strings, which means that numbers are not sorted numerically:
> [-1, -20, 7, 50].sort() [ -1, -20, 50, 7 ]
You can fix this by providing the optional parameter compareFunction
, which controls how sorting is done. It has the following signature:
function
compareFunction
(
a
,
b
)
This function compares a
and b
and returns:
-1
) if a
is less than b
a
is equal to b
1
) if a
is greater than b
For numbers, you can simply return a-b
, but that can cause numeric overflow. To prevent that from happening, you need more verbose code:
function
compareCanonically
(
a
,
b
)
{
if
(
a
<
b
)
{
return
-
1
;
}
else
if
(
a
>
b
)
{
return
1
;
}
else
{
return
0
;
}
}
I don’t like nested conditional operators. But in this case, the code is so much less verbose that I’m tempted to recommend it:
function
compareCanonically
(
a
,
b
)
{
return
a
<
b
?
-
1
:
(
a
>
b
?
1
:
0
);
}
Using the function:
> [-1, -20, 7, 50].sort(compareCanonically) [ -20, -1, 7, 50 ]
For strings, you can use String.prototype.localeCompare
(see Comparing Strings):
> ['c', 'a', 'b'].sort(function (a,b) { return a.localeCompare(b) }) [ 'a', 'b', 'c' ]
The parameter compareFunction
is also useful for sorting objects:
var
arr
=
[
{
name
:
'Tarzan'
},
{
name
:
'Cheeta'
},
{
name
:
'Jane'
}
];
function
compareNames
(
a
,
b
)
{
return
a
.
name
.
localeCompare
(
b
.
name
);
}
With compareNames
as the compare function, arr
is sorted by name
:
> arr.sort(compareNames) [ { name: 'Cheeta' }, { name: 'Jane' }, { name: 'Tarzan' } ]
The following methods perform various nondestructive operations on arrays:
Array.prototype.concat(arr1?, arr2?, ...)
Creates a new array that contains all the elements of the receiver, followed by all the elements of the array arr1
, and so on. If one of the parameters is not an array, then it is added to the result as an element (for example, the first argument, 'c'
, here):
> var arr = [ 'a', 'b' ]; > arr.concat('c', ['d', 'e']) [ 'a', 'b', 'c', 'd', 'e' ]
The array that concat()
is invoked on is not changed:
> arr [ 'a', 'b' ]
Array.prototype.slice(begin?, end?)
Copies array elements into a new array, starting at begin
, until and excluding the element at end
:
> [ 'a', 'b', 'c', 'd' ].slice(1, 3) [ 'b', 'c' ]
If end
is missing, the array length is used:
> [ 'a', 'b', 'c', 'd' ].slice(1) [ 'b', 'c', 'd' ]
If both indices are missing, the array is copied:
> [ 'a', 'b', 'c', 'd' ].slice() [ 'a', 'b', 'c', 'd' ]
If either of the indices is negative, the array length is added to it. Thus, -1
refers to the last element, and so on:
> [ 'a', 'b', 'c', 'd' ].slice(1, -1) [ 'b', 'c' ] > [ 'a', 'b', 'c', 'd' ].slice(-2) [ 'c', 'd' ]
Array.prototype.join(separator?)
Creates a string by applying toString()
to all array elements and putting the string in separator
between the results. If separator
is omitted, ','
is used:
> [3, 4, 5].join('-') '3-4-5' > [3, 4, 5].join() '3,4,5' > [3, 4, 5].join('') '345'
join()
converts undefined
and null
to empty strings:
> [undefined, null].join('#') '#'
Holes in arrays are also converted to empty strings:
> ['a',, 'b'].join('-') 'a--b'
The following methods search for values in arrays:
Array.prototype.indexOf(searchValue, startIndex?)
> [ 3, 1, 17, 1, 4 ].indexOf(1) 1 > [ 3, 1, 17, 1, 4 ].indexOf(1, 2) 3
Strict equality (see Equality Operators: === Versus ==) is used for the search, which means that indexOf()
can’t find NaN
:
> [NaN].indexOf(NaN) -1
Array.prototype.lastIndexOf(searchElement, startIndex?)
Searches the array for searchElement
, starting at startIndex
, backward. It returns the index of the first occurrence or –1 if nothing is found. If startIndex
is negative, the array length is added to it; if it is missing, the whole array is searched. Strict equality (see Equality Operators: === Versus ==) is used for the search:
> [ 3, 1, 17, 1, 4 ].lastIndexOf(1) 3 > [ 3, 1, 17, 1, 4 ].lastIndexOf(1, -3) 1
Iteration methods use a function to iterate over an array. I distinguish three kinds of iteration methods, all of which are nondestructive: examination methods mainly observe the content of an array; transformation methods derive a new array from the receiver; and reduction methods compute a result based on the receiver’s elements.
Each method described in this section looks like this:
arr
.
examinationMethod
(
callback
,
thisValue
?
)
Such a method takes the following parameters:
callback
is its first parameter, a function that it calls.
Depending on the examination method, the callback returns a boolean or nothing. It has the following signature:
function
callback
(
element
,
index
,
array
)
element
is an array element for callback
to process, index
is the element’s index, and array
is the array that examinationMethod
has been invoked on.
thisValue
allows you to configure the value of this
inside callback
.
And now for the examination methods whose signatures I have just described:
Array.prototype.forEach(callback, thisValue?)
Iterates over the elements of an array:
var
arr
=
[
'apple'
,
'pear'
,
'orange'
];
arr
.
forEach
(
function
(
elem
)
{
console
.
log
(
elem
);
});
Array.prototype.every(callback, thisValue?)
This example checks whether every number in the array is even:
> function isEven(x) { return x % 2 === 0 } > [ 2, 4, 6 ].every(isEven) true > [ 2, 3, 4 ].every(isEven) false
If the array is empty, the result is true
(and callback
is not called):
> [].every(function () { throw new Error() }) true
Array.prototype.some(callback, thisValue?)
This example checks whether there is an even number in the array:
> function isEven(x) { return x % 2 === 0 } > [ 1, 3, 5 ].some(isEven) false > [ 1, 2, 3 ].some(isEven) true
If the array is empty, the result is false
(and callback
is not called):
> [].some(function () { throw new Error() }) false
One potential pitfall of forEach()
is that it does not support break
or something similar to prematurely abort the loop. If you need to do that, you can use some()
:
function
breakAtEmptyString
(
strArr
)
{
strArr
.
some
(
function
(
elem
)
{
if
(
elem
.
length
===
0
)
{
return
true
;
// break
}
console
.
log
(
elem
);
// implicit: return undefined (interpreted as false)
});
}
some()
returns true
if a break happened, and false
otherwise. This allows you to react differently depending on whether iterating finished successfully (something that is slightly tricky with for
loops).
Transformation methods take an input array and produce an output array, while the callback controls how the output is produced. The callback has the same signature as for examination:
function
callback
(
element
,
index
,
array
)
There are two transformation methods:
Array.prototype.map(callback, thisValue?)
Each output array element is the result of applying callback
to an input element. For example:
> [ 1, 2, 3 ].map(function (x) { return 2 * x }) [ 2, 4, 6 ]
Array.prototype.filter(callback, thisValue?)
The output array contains only those input elements for which callback
returns true
. For example:
> [ 1, 0, 3, 0 ].filter(function (x) { return x !== 0 }) [ 1, 3 ]
For reducing, the callback has a different signature:
function
callback
(
previousValue
,
currentElement
,
currentIndex
,
array
)
The parameter previousValue
is the value previously returned by the callback. When the callback is first called, there are two possibilities (the descriptions are for Array.prototype.reduce()
; differences with reduceRight()
are mentioned in parentheses):
initialValue
has been provided. Then previousValue
is initialValue
, and currentElement
is the first array element (reduceRight
: the last array element).
initialValue
has been provided. Then previousValue
is the first array element, and currentElement
is the second array element (reduceRight
: the last array element and second-to-last array element).
There are two reduction methods:
Array.prototype.reduce(callback, initialValue?)
function
add
(
prev
,
cur
)
{
return
prev
+
cur
;
}
console
.
log
([
10
,
3
,
-
1
].
reduce
(
add
));
// 12
If you invoke reduce
on an array with a single element, that element is returned:
> [7].reduce(add) 7
If you invoke reduce
on an empty array, you must specify initialValue
, otherwise you get an exception:
> [].reduce(add) TypeError: Reduce of empty array with no initial value > [].reduce(add, 123) 123
Array.prototype.reduceRight(callback, initialValue?)
reduce()
, but iterates from right to left.
In many functional programming languages, reduce
is known as fold
or foldl
(left fold) and reduceRight
is known as foldr
(right fold).
Another way to look at the reduce
method is that it implements an n-ary operator OP
:
OP1≤i≤n
xi
via a series of applications of a binary operator op2
:
(...(x1 op2
x2) op2
...) op2
xn
That’s what happened in the previous code example: we implemented an n-ary sum operator for arrays via JavaScript’s binary plus operator.
As an example, let’s examine the two iteration directions via the following function:
function
printArgs
(
prev
,
cur
,
i
)
{
console
.
log
(
'prev:'
+
prev
+
', cur:'
+
cur
+
', i:'
+
i
);
return
prev
+
cur
;
}
As expected, reduce()
iterates from left to right:
> ['a', 'b', 'c'].reduce(printArgs) prev:a, cur:b, i:1 prev:ab, cur:c, i:2 'abc' > ['a', 'b', 'c'].reduce(printArgs, 'x') prev:x, cur:a, i:0 prev:xa, cur:b, i:1 prev:xab, cur:c, i:2 'xabc'
And reduceRight()
iterates from right to left:
> ['a', 'b', 'c'].reduceRight(printArgs) prev:c, cur:b, i:1 prev:cb, cur:a, i:0 'cba' > ['a', 'b', 'c'].reduceRight(printArgs, 'x') prev:x, cur:c, i:2 prev:xc, cur:b, i:1 prev:xcb, cur:a, i:0 'xcba'
Some objects in JavaScript look like an array, but they aren’t one. That usually means that they have indexed access and a length
property, but none of the array methods. Examples include the special variable arguments
, DOM node lists, and strings. Array-Like Objects and Generic Methods gives tips for working with array-like objects.
To iterate over an array arr
, you have two options:
A simple for
loop (see for):
for
(
var
i
=
0
;
i
<
arr
.
length
;
i
++
)
{
console
.
log
(
arr
[
i
]);
}
One of the array iteration methods (see Iteration (Nondestructive)). For example, forEach()
:
arr
.
forEach
(
function
(
elem
)
{
console
.
log
(
elem
);
});
Do not use the for-in
loop (see for-in) to iterate over arrays. It iterates over indices, not over values. And it includes the keys of normal properties while doing so, including inherited ones.