Loop through an array in Javascript
Published on 05/10/2018
7 min read
In category
javascript
Iterating arrays in Javascript
For a long time, in the era of ECMAScript 3 (1999 - 2008), the for..in
statement was very often misused to iterate over array objects, this
statement is really meant to enumerate object properties, no question why
many people reported strange behaviors. For a newbie or a
foreigner of the Javascript world, coming from other languages, the for-in
statement might look just good but it shouldn't be used for this.
The simplest way I long suggested was to use a plain sequential for
loop:
let array = ["Hello","World"];
for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}
And this was the most common way to iterate array objects, of course you can
also use any other sequential loop like while
, do..while
, etc...
forEach
Enter ECMAScript 5 The ECMAScript 5 Specification (ES5 for short) introduced a lot of very helpful array methods, one of them, the Array.prototype.forEach and it gives us a very short way to iterate over an array:
array.forEach(function (item) {
console.log(item);
});
Being almost 9 years as the time of writing that the ES5 spec. was released (Dec. 2009), it has been implemented by almost all modern engines in the desktop, server and mobile environments, so it's safe to use them.
And with the ES6 arrow function syntax, it's even more succinct:
array.forEach(item => console.log(item));
Very short and sweet.
Arrow functions are also being widely implemented, unless you plan to support very old platforms (e.g. IE11) you are also safe to go.
The forEach
method accepts two parameters, the first a callback function,
and the second one an optional thisArg, which in case provided, will set
the this
value of the callback function if possible (e.g. it not possible
to bind the this
value of arrow functions).
The callback function passed to forEach
will actually receive 3 arguments
when called for each element and they are:
- The current element
- The index of the element
- A reference to the array object being iterated
For example, the following:
var colors = ['red', 'green', 'blue'];
colors.forEach(function (c, i, array) {
console.log(c, i, array);1
});
Will log:
red 0 Array(3) [ "red", "green", "blue" ]
green 1 Array(3) [ "red", "green", "blue" ]
blue 2 Array(3) [ "red", "green", "blue" ]
However there are some differences with a sequential loop, for instance, the
forEach
method lacks the ability to stop its execution at an arbitrary
point, thing that we can easily do with the break
statement. Also you
cannot return from the enclosing function if you wanted, since the return
value of the callback is simply ignored.
Another difference is that if we have an sparse array, non existent
elements are skipped by the forEach
method:
var array = [1, , , , 2];
array.forEach((el, i) => console.log(el, i));
// Will log:
// 1 0
// 2 4
Sparse arrays or arrays with holes are by definition, cases in which some
of the index properties are missing from the object itself, and there is no
continuos sequence from 0
to length - 1
.
for-of
Enter ECMAScript 6 The ES6 standard introduces the concept of iterable objects and defined a new
construct for traversing data, the for...of
statement.
Iterables and generators are a big piece of ES6 that deserve to have its own article.
Array objects are by definition built-in iterables in ES6, so you can use this statement on them:
let colors = ['red', 'green', 'blue'];
for (const color of colors){
console.log(color);
}
Since it's a ES6 statement, we can use more of its features, like let
and
const
bindings.
The for-of
statement is also widely implemented in mature platforms, so
you can use it if you don't plan to target old engines.
for-in
?
Why not use Coming back to the for-in
statement, it should be avoided to iterate
arrays because as I said earlier, this statement is really meant to
enumerate object properties.
It can give unexpected results if you use it to iterate over arrays because:
- The order of enumeration is not guaranteed by the specification, the array indexes may not be visited in numeric order.
- Inherited properties are also enumerated.
The second point can give you a lot of problems, for example, if
the Array.prototype
object is extended to include a method there, that
property will be also enumerated.
For example:
// Don't do this!
Array.prototype.test = "inherited!";
var array = ['a', 'b', 'c'];
array.prop = 'prop';
for (var i in array) {
console.log(array[i]);
}
The above code will log, "a", "b", "c" "prop" and "inherited!".
That was a big problem in the past, particularly if you used some library that relied heavily on native prototypes augmentation (such as early versions of MooTools for example).
The for-in statement as I said before is there to enumerate object properties, for example:
var obj = {
"a": 1,
"b": 2,
"c": 3
};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
// or if (Object.prototype.hasOwnProperty.call(obj,prop)) for safety...
console.log("prop: " + prop + " value: " + obj[prop])
}
}
In the above example the hasOwnProperty
method allows you to enumerate only
own properties, that's it, only the properties that the object physically
has, no inherited properties.
Now with ECMAScript 5 we have the Object.keys
method to get the enumerable
own properties.
Conclusions
If you can target ES6, use the for-of
statement, otherwise use the
Array.prototype.forEach
method, but if you want to have flow control on the
iteration you want to use a traditional for
loop.
Here are the pros and cons of each one of them:
for-of
-
Pros
- Clean and easy to read.
- Can use
let
andconst
ES6 bindings, this eliminates possible closure problems when working with functions within loops. - Can use flow control statements like
break
,continue
andlabels
to potentially manage complex nested looping. - Can return from the enclosing function.
- Can iterate over array like objects, like the
arguments
object,strings
,Map
s, other built-in iterables and custom iterators.
-
Cons
- Requires you to be able to target ES6
Array.prototype.forEach
-
Pros
- Clean and easy to read.
- Provides the element index if needed without needing to keep track of incrementing a variable.
-
Cons
- Cannot have complete control of its flow since we cannot use
break
,continue
orlabels
. - Cannot return from the enclosing function.
- Cannot have complete control of its flow since we cannot use
for
or any traditional sequential loop
-
Pros
- Works on every platform.
- Can be potentially the fastest way if performance is critical.
- Can use flow control statements like
break
,continue
andlabels
. - Can return from the enclosing function.
-
Cons
- Some might consider it less "elegant" and harder to read.
- Need to keep track and increment the index variable.
And remember, enumeration is not iteration!
Recommended read: