## Chapter 18. Arrays

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.

## Overview

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' ]```

### Arrays Are Maps, Not Tuples

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 Can Also Have Properties

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```

## Creating Arrays

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```

### The Array Constructor

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.

#### Creating an empty array with a given length

An empty array with a given length has only holes in it! Thus, it rarely makes sense to use this version of the 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!

#### Initializing an array with elements (avoid!)

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' ]```

### Multidimensional Arrays

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` `=` `[` `[``'.'``,``'.'``,``'.'``],` `[``'.'``,``'.'``,``'.'``],` `[``'.'``,``'.'``,``'.'``]` `];``

## Array Indices

When you are working with array indices, you must keep in mind the following limits:

• Indices are numbers i in the range 0 ≤ `i` < 232−1.
• The maximum length is 232−1.

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 and Indices

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```

### Deleting Array Elements

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' ]```

### Tip

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:

• A property key `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.
• An array property whose key is an array index is called an element.

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:

1. Convert `P` to a number.
2. Convert the number to a 32-bit unsigned integer.
3. Convert the integer to a string.

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```

## length

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

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```

### Decreasing the Length of an Array

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```

#### Clearing an array

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` `=` `[];``

#### Clearing shared arrays

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 Length

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```

## Holes in Arrays

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`.

### Tip

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.

### Creating Holes

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```

### Warning

You need two trailing commas to create a trailing hole, because the last comma is always ignored:

```> [ 'a', ].length
1
> [ 'a', ,].length
2```

### Sparse Arrays Versus Dense Arrays

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```

### Which Operations Ignore Holes, and Which Consider Them?

Some operations involving arrays ignore holes, while others consider them. This sections explains the details.

#### Array iteration methods

`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' ]```

#### Other array methods

`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

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```

#### Function.prototype.apply()

`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 ]```

### Warning

`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.

### Removing Holes from Arrays

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 Constructor Method

`Array.isArray(obj)`
Returns `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)).

## Array Prototype Methods

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).

## Adding and Removing Elements (Destructive)

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?, ...)`

Starting at `start`, removes `deleteCount` elements and inserts the elements given. In other words, you are replacing the `deleteCount` elements at position `start` with `elem1`, `elem2`, and so on. The method returns the elements that have been removed:

```> 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' ]```

## Sorting and Reversing Elements (Destructive)

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:

• An integer less than zero (e.g., `-1`) if `a` is less than `b`
• Zero if `a` is equal to `b`
• An integer greater than zero (e.g., `1`) if `a` is greater than `b`

### Comparing Numbers

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` `return` `a` `<` `b` `?` `-``1` `(``a` `>` `b` `?` `1` `:` `0``);`
`}````

Using the function:

```> [-1, -20, 7, 50].sort(compareCanonically)
[ -20, -1, 7, 50 ]```

### Comparing Strings

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' ]```

### Comparing Objects

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' } ]```

## Concatenating, Slicing, Joining (Nondestructive)

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'```

## Searching for Values (Nondestructive)

The following methods search for values in arrays:

`Array.prototype.indexOf(searchValue, startIndex?)`

Searches the array for `searchValue`, starting at `startIndex`. 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:

```> [ 3, 1, 17, 1, 4 ].indexOf(1)
1
> [ 3, 1, 17, 1, 4 ].indexOf(1, 2)
3```

Strict equality (seevEquality 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 (Nondestructive)

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.

### Examination Methods

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?)`

Returns `true` if the callback returns `true` for every element. It stops iteration as soon as the callback returns `false`. Note that not returning a value leads to an implicit return of `undefined`, which `every()` interprets as `false`. `every()` works like the universal quantifier (“for all”).

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?)`

Returns `true` if the callback returns `true` for at least one element. It stops iteration as soon as the callback returns `true`. Note that not returning a value leads to an implicit return of `undefined`, which `some` interprets as `false`. `some()` works like the existential quantifier (“there exists”).

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

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 ]```

### Reduction Methods

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):

• An explicit `initialValue` has been provided. Then `previousValue` is `initialValue`, and `currentElement` is the first array element (`reduceRight`: the last array element).
• No explicit `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?)`

Iterates from left to right and invokes the callback as previously sketched. The result of the method is the last value returned by the callback. This example computes the sum of all array elements:

````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
123```
`Array.prototype.reduceRight(callback, initialValue?)`
Works the same as `reduce()`, but iterates from right to left.

### Note

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'```

## Pitfall: Array-Like Objects

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.

## Best Practices: Iterating over Arrays

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.

Get Speaking JavaScript now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.