JavaScript – ECMAScript 5 Array Methods

ECMAScript 5 defines nine new array methods for iterating,
mapping, filtering, testing, reducing, and searching arrays. The
subsections below describe these methods.

Before we cover the details, however, it is worth making some
generalizations about these ECMAScript 5 array methods. First, most of
the methods accept a function as their first argument and invoke that
function once for each element (or some elements) of the array. If the
array is sparse, the function you pass is not invoked for nonexistent
elements. In most cases, the function you supply is invoked with three
arguments: the value of the array element, the index of the array
element, and the array itself. Often, you only need the first of these
argument values and can ignore the second and third values. Most of
the ECMAScript 5 array methods that accept a function as their first
argument accept an optional second argument. If specified, the
function is invoked as if it is a method of this second argument. That
is, the second argument you pass becomes the value of the this keyword inside of the function you
pass. The return value of the function you pass is important, but
different methods handle the return value in different ways. None of
the ECMAScript 5 array methods modify the array on which they are
invoked. If you pass a function to these methods, that function may
modify the array, of course.

forEach()

The forEach() method
iterates through an array, invoking a function you specify for each
element. As described above, you pass the function as the first
argument to for Each(). forEach() then invokes your function with
three arguments: the value of the array element, the index of the
array element, and the array itself. If you only care about the
value of the array element, you can write a function with only one
parameter—the additional arguments will be ignored:

var data = [1,2,3,4,5];                           // An array to sum
// Compute the sum of the array elements
var sum = 0;                                      // Start at 0
data.forEach(function(value) { sum += value; });  // Add each value to sum
sum                                               // => 15

// Now increment each array element
data.forEach(function(v, i, a) { a[i] = v + 1; });
data                                              // => [2,3,4,5,6]

Note that forEach() does
not provide a way to terminate iteration before all elements have
been passed to the function. That is, there is no equivalent of the
break statement you can use with
a regular for loop. If you need
to terminate early, you must throw an exception, and place the call
to forEach() within a try block. The following code defines a
foreach() function that calls the
forEach() method within such a
try block. If the function passed to foreach() throws foreach.break, the loop will terminate
early:

function foreach(a,f,t) {
    try { a.forEach(f,t); }
    catch(e) {
	if (e === foreach.break) return; 
	else throw e;
    }
}
foreach.break = new Error("StopIteration");

map()

The map() method passes
each element of the array on which it is invoked to the function you
specify, and returns an array containing the values returned by that
function. For example:

a = [1, 2, 3];
b = a.map(function(x) { return x*x; });  // b is [1, 4, 9]

The function you pass to map() is invoked in the same way as a
function passed to forEach(). For
the map() method, however, the
function you pass should return a value. Note that map() returns a new array: it does not
modify the array it is invoked on. If that array is sparse, the
returned array will be sparse in the same way: it will have the same
length and the same missing elements.

filter()

The filter() method returns
an array containing a subset of the elements of the array on which
it is invoked. The function you pass to it should be predicate: a
function that returns true or
false. The predicate is invoked
just as for forEach() and
map(). If the return value is
true, or a value that converts to
true, then the element passed to
the predicate is a member of the subset and is added to the array
that will become the return value. Examples:

a = [5, 4, 3, 2, 1];
smallvalues = a.filter(function(x) { return x < 3 });   // [2, 1]
everyother = a.filter(function(x,i) { return i%2==0 }); // [5, 3, 1]

Note that filter() skips
missing elements in sparse arrays, and that its return value is
always dense. To close the gaps in a sparse array, you can do
this:

var dense = sparse.filter(function() { return true; });

And to close gaps and remove undefined and null elements you
can use filter like this:

a = a.filter(function(x) { return x !== undefined && x != null; });

every() and some()

The every() and some() methods are array predicates: they
apply a predicate function you specify to the elements of the array,
and then return true or false.

The every() method is like
the mathematical “for all” quantifier ∀: it returns true if and only if your predicate
function returns true for all
elements in the array:

a = [1,2,3,4,5];
a.every(function(x) { return x < 10; })      // => true: all values < 10.
a.every(function(x) { return x % 2 === 0; }) // => false: not all values even.

The some() method is like
the mathematical “there exists” quantifier ∃: it returns true if there exists at least one element
in the array for which the predicate returns true, and returns false if and only if the predicate returns
false for all elements of the array:

a = [1,2,3,4,5];
a.some(function(x) { return x%2===0; })  // => true a has some even numbers.
a.some(isNaN)                            // => false: a has no non-numbers.

Note that both every() and
some() stop iterating array
elements as soon as they know what value to return. some() returns true the first time your predicate returns
true, and only iterates through the entire array if your predicate
always returns false. every() is the opposite: it returns
false the first time your
predicate returns false, and only
iterates all elements if your predicate always returns true. Note also that by mathematical
convention, every() returns
true and some returns false when invoked on an empty
array.

reduce(), reduceRight()

The reduce() and reduceRight() methods combine the elements
of an array, using the function you specify, to produce a single
value. This is a common operation in functional programming and also
goes by the names “inject” and “fold.” Examples help illustrate how
it works:

var a = [1,2,3,4,5]
var sum = a.reduce(function(x,y) { return x+y }, 0);     // Sum of values
var product = a.reduce(function(x,y) { return x*y }, 1); // Product of values
var max = a.reduce(function(x,y) { return (x>y)?x:y; }); // Largest value

reduce() takes two
arguments. The first is the function that performs the reduction
operation. The task of this reduction function is to somehow combine
or reduce two values into a single value, and to return that reduced
value. In the examples above, the functions combine two values by
adding them, multiplying them, and choosing the largest. The second
(optional) argument is an initial value to pass to the
function.

Functions used with reduce() are different than the functions
used with forEach() and map(). The familiar value, index, and
array values are passed as the second, third, and fourth arguments.
The first argument is the accumulated result of the reduction so
far. On the first call to the function, this first argument is the
initial value you passed as the second argument to reduce(). On subsequent calls, it is the
value returned by the previous invocation of the function. In the
first example above, the reduction function is first called with
arguments 0 and 1. It adds these and returns 1. It is then called
again with arguments 1 and 2 and it returns 3. Next it computes
3+3=6, then 6+4=10, and finally 10+5=15. This final value, 15,
becomes the return value of reduce().

You may have noticed that the third call to reduce() above has only a single argument:
there is no initial value specified. When you invoke reduce() like this with no initial value,
it uses the first element of the array as the initial value. This
means that the first call to
the reduction function will have the first and second array elements
as its first and second arguments. In the sum and product examples
above, we could have omitted the initial value argument.

Calling reduce() on an
empty array with no initial value argument causes a TypeError. If
you call it with only one value—either an array with one element and
no initial value or an empty array and an initial value—it simply
returns that one value without ever calling the reduction
function.

reduceRight() works just
like reduce(), except that it
processes the array from highest index to lowest (right-to-left),
rather than from lowest to highest. You might want to do this if the
reduction operation has right-to-left precedence, for
example:

var a = [2, 3, 4]
// Compute 2^(3^4).  Exponentiation has right-to-left precedence
var big = a.reduceRight(function(accumulator,value) {
                            return Math.pow(value,accumulator);
                        });

Note that neither reduce()
nor reduceRight() accepts an
optional argument that specifies the this value on which the reduction function
is to be invoked. The optional initial value argument takes its
place. See the Function.bind()
method if you need your reduction function invoked as a method of a
particular object.

It is worth noting that the every() and some() methods described above perform a
kind of array reduction operation. They differ from reduce(), however, in that they terminate
early when possible, and do not always visit every array
element.

The examples shown so far have been numeric for simplicity,
but reduce() and reduceRight() are not intended solely for
mathematical computations. Consider the union() function from Example 6-2. It computes the “union” of two objects and
returns a new object that has the properties of both. This function
expects two objects and returns another object, so it works as a
reduction function, and we can use reduce() to generalize it and compute the
union of any number of objects:

var objects = [{x:1}, {y:2}, {z:3}];
var merged = objects.reduce(union);    // => {x:1, y:2, z:3}

Recall that when two objects have properties with the same
name, the union() function uses
the value of that property from the first argument. Thus reduce() and reduceRight() may give different results
when used with union():

var objects = [{x:1,a:1}, {y:2,a:2}, {z:3,a:3}];
var leftunion = objects.reduce(union);       // {x:1, y:2, z:3, a:1}
var rightunion = objects.reduceRight(union); // {x:1, y:2, z:3, a:3}

indexOf() and lastIndexOf()

indexOf() and lastIndexOf() search an array for an
element with a specified value, and return the index of the first
such element found, or –1 if none is found. indexOf() searches the array from
beginning to end, and lastIndexOf() searches from end to
beginning.

a = [0,1,2,1,0];
a.indexOf(1)       // => 1: a[1] is 1
a.lastIndexOf(1)   // => 3: a[3] is 1
a.indexOf(3)       // => -1: no element has value 3

Unlike the other methods described in this section, indexOf() and lastIndexOf() do not take a function
argument. The first argument is the value to search for. The second
argument is optional: it specifies the array index at which to begin
the search. If this argument is omitted, indexOf() starts at the beginning and
lastIndexOf() starts at the end.
Negative values are allowed for the second argument and are treated
as an offset from the end of the array, as they are for the splice() method: a value of –1, for
example, specifies the last element of the array.

The following function searches an array for a specified value
and returns an array of all matching indexes.
This demonstrates how the second argument to indexOf() can be used to find matches
beyond the first.

// Find all occurrences of a value x in an array a and return an array
// of matching indexes
function findall(a, x) {
    var results = [],            // The array of indexes we'll return
        len = a.length,          // The length of the array to be searched
        pos = 0;                 // The position to search from
    while(pos < len) {           // While more elements to search...
        pos = a.indexOf(x, pos); // Search
        if (pos === -1) break;   // If nothing found, we're done.
        results.push(pos);       // Otherwise, store index in array
        pos = pos + 1;           // And start next search at next element
    }
    return results;              // Return array of indexes
}

Note that strings have indexOf() and lastIndexOf() methods that work like these
array methods.

Comments are closed.