Running Through Arrays
If you're new to JavaScript (or it's been a while since you looked at array iteration in JavaScript), you probably want to understand all the options that are available to you.
NOTE: This article assumes a familiarity with ES6 arrow
functions and the let
keyword.
Old School
One of the first concepts taught with arrays is iteration -
looping through each element in the array. Historically, this
has been done in JavaScript using a standard for
loop.
let array = ["a", "b", "c"]; for (let index = 0; index < array.length; ++index) { let element = array[index]; console.log(element); }
If you don't want to be bothered with the index
variable and boundary condition checking, you can use the
forEach
method.
let array = ["a", "b", "c"]; array.forEach(element => { console.log(element); });
New School
This is how things were done for a long time, but ES6 added
a new for...of
statement:
let array = ["a", "b", "c"]; for (let element of array) { console.log(element); }
The new statement provides a simpler approach to iterating
over the elements in the array. Using for...of
,
you don't need to worry about the index variable or the
array boundaries and you also don't need the additional
function that's required by forEach
. But what
if you want the index value for each element?
let array = ["a", "b", "c"]; for (let [index, element] of array.entries()) { console.log(index, element); }
For each element in your array, the entities
function will generate another array containing the element's
index position and a reference to the element. The example
above uses destructuring to
assign those values to the index
and
element
variables.
Specialization
Although they are not new, JavaScript provides three higher-order
functions for iterating over arrays: filter
,
map
, and reduce
. All three functions
operate on an existing array and repackage it's elements in some
way.
Filter
The filter
function provides a way to selectively
remove elements from an array by evaluating each element and
generating a boolean result. Let's say we have a list of names
that we want to filter so it only includes people whose name
begins with "J". We could use a standard "old-school" loop.
let names = ["Amy", "Fred", "John", "Jill", "Kim", "Steve", "Ted"]; for (let index = names.length - 1; index >= 0; --index) { if (!names[index].startsWith("J")) { names.splice(index, 1); } }
Using the filter
function, we can provide a much
simpler solution. We call the filter function using our array,
and pass a function that evaluates a single element and returns
true
if the element should remain in the array or
false
if the element should be removed (filtered).
The function will be called once for each element in the array.
let names = ["Amy", "Fred", "John", "Jill", "Kim", "Steve", "Ted"]; names = names.filter(name => name.startsWith("J"));
Not only is the second method simpler, it also avoids fussing with the logic that looped through our first array backwards to avoid problems with removing elements while also iterating the array.
Map
The map
function allows us to transform each
element in the array into a new element in another array. For
example, we could take our list of names and transform each
element into an HTML list item. First, we'll go old-school.
let names = ["Amy", "Fred", "John", "Jill", "Kim", "Steve", "Ted"]; let elements = []; for (let index = 0; index < names.length; ++index) { let element = document.createElement("li"); element.innerText = names[index]; elements.push(element); }
Then we'll update the example using map
. We will need
to provide a function that takes the name string and returns a
newly-created <li>
element. Each element that's
returned from this function will be appended to a new array.
Again, the function will be called once for each element in the
array.
let names = ["Amy", "Fred", "John", "Jill", "Kim", "Steve", "Ted"]; let elements = names.map(name => { let element = document.createElement("li"); element.innerText = name; return element; });
Voila! We've transformed an array of names into an array of list items.
Reduce
The reduce
function can take a little getting used
to. It will evaluate each element in the array and generate a
single aggregated result based on all the elements. In this
example, let's consider an array of hiking objects. Each object
has a distance
property and we want to determine
the total distance for all hikes.
let hikes = [{ location: "Boston", distance: 5.1 }, { location: "Cleveland", distance: 2.3 }, { location: "Houston", distance: 7.6 }]; let totalDistance = 0; for (let index = 0; index < hikes.length; ++index) { totalDistance += hikes[index].distance }
The reduce
function will again take a function as
it's first parameter, but the function receives two values - the
aggregated result so far, and an element from the array. The
second parameter for the reduce
function provides
an initial value for the aggregated result.
let initialDistance = 0; let totalDistance = hikes.reduce((partialResult, hike) => { return partialResult + hike.distance; }, initialDistance);
Other examples of using reduce might count the number of elements in an array that meet some condition, or it might aggregate several objects into a "summary" object.
Summary
If you've only been using simple for
loops with
index variables to iterate arrays, now is a great time to
begin experimenting with some alternatives. Here are a few
exercises you can work through to become more familiar with
these other approaches to iteration.
- Write your own filter function that uses
for...of
to iterate an array and remove unwanted elements. Your filter function can take two parameters: an input array, and a filtering function that receives a single element as the input and returns a boolean value to indicate whether or not to filter the element. The filter function should return a new array. - Write your own map function that uses
for...of
to iterate an array and return a new array of transformed elements. Your map function should take two parameters: an input array, and a transform function that receives a single element as the input and returns a transformed element. The map function should return a new array. - I'll let you guess what the third exercise is...
- How would you use the three higher-order
Array
methods to implement other iterativeArray
methods, such asArray.every()
,Array.find()
, andArray.some()
? (MDN provides documentation on the variousArray
methods.)