Going Loopy With JavaScript, jQuery and LoDash
There are different ways to loop over arrays in JavaScript, but it can be difficult choosing the right one.
Plus keeping each method straight can drive a developer nuts.
There is a classic JavaScript for loop, JavaScript forEach method and a collection of libraries with forEach and each helper methods. And don't forget a JavaScript object is just a special array and you can iterate over its members as well.
You will learn different ways to loop or iterate JavaScript arrays, including how to use associative arrays or objects to find the values you want.
One of the most fundamental programming constructs is the for loop. All languages have some implementation of the for loop and forEach and JavaScript is no different.
- Basic For Loop
- JavaScript Array forEach
- Lodash forEach
- jQuery each()
- Iterating Over an Associative Array
- Summary
As the language has matured so have our options to loop over arrays and objects. JavaScript objects are also arrays, which makes for a clean solution to index values by a key or name.
The Basic For Loop
JavaScript for loops iterate over each item in an array. JavaScript arrays are zero based, which means the first item is referenced with an index of 0.
Referencing items in arrays is done with a numeric index, starting at zero and ending with the array length minus 1. The syntax to access an array member
for (initialization; condition; final expression) { // code to be executed}
As you can see the for loop statement uses three expressions: the initialization, the condition, and the final expression. The final expression is executed at the end of each loop execution. It is commonly used to increment the index.
The following is an example of using JavaScript to loop through an array.
let myArray = ["one", "two", "three", "four"];
for(let i = 0; i < myArray.length; i++){
console.log(myArray[i]);
}
If you need to iterate through a second level property or field you can nest for loops. The biggest thing you need to keep track of is declaring a separate index variable.
let myArray = [{"child": ["one", "two", "three", "four"]},
{"child": ["five", "six", "seven", "eight"]}];
for(let i = 0; i < myArray.length; i++){
let childArray = myArray[i].child;
for(let j = 0; j < childArray.length; j++){
console.log(childArray[j]);
}
}/* Outputs:onetwothreefourfivesixseveneight*/
If you need to stop a loop you can use the break statement, or set the index to the length of the array or to a value that makes the statement no longer true.
var myArray = [...];
for(let i = 0; i < myArray.length; i++){
if([condition met]){
break;
}
console.log(myArray[i]);
}
If you omit the condition statement you must use a break to terminate the for loop. Otherwise it creates an infinite loop since the criteria is never met.
The JavaScript Array forEach Method
The traditional for loop is easy to understand, but sometimes the syntax can be tedious. For example, the nested loop requires new variable declarations with a nested scope. Modern JavaScript has added a forEach method to the native array object.
Array.prototype.forEach(callback([value, index, array]), thisArg)
This method is a member of the array prototype and uses a callback function for you to embed any custom logic to the iteration.
The forEach callback function actually accepts three arguments:
- element value
- element index
- array being traversed (has anyone ever used this???)
The element is the current item's value. Think of it as myArray[i]. The next value is the index, which would correlate to i in the previous examples.
The last parameter is a copy of the array being iterated through. I can see how this could be useful, but often I see it ignored. This is a nice to have, not something you need to worry about.
If you don't declare a variable for any of these parameters they just won't be allocated. For example, most uses of the forEach method just use the element, and ignore the index and array. The element is the most important value when iterating, so you should always declare an element parameter.
After the callback method the forEach method also accepts an optional argument, thisArg. If supplied it is used as the this reference within the callback method. Otherwise this is undefined.
myArray.forEach(function(element) {
console.log(element);
});/* outputs:onetwothreefour*/
If you want to count, you need to declare a variable outside the method's scope.
let count = 0;myArray.forEach(function(element, i) {
count++;
});
console.log("forEach processed: ", count, " elements.");// forEach processed: 4 elements.
A drawback to using forEach is it cannot be stopped, like a break statement in the for loop. You could throw an exception, but that is an extreme case.
You should also be aware if the array items are changed while the forEach method is executing the changes are not processed. The array is the same values from the point the forEach execution began.
However, if an item is deleted or removed from the array during the forEach operation it will not be processed by the forEach callback.
forEach does not modify the array itself, the callback method can. The method returns undefined and cannot be chained like some other array methods.
forEach only works on arrays, which means you need to be a little creative if you want to iterate over objects.
There is a NodeList forEach polyfil you might consider loading if you still have legacy browsers to support. But browser support is very broad since the forEach method was added in ES5.
Element Lists and forEach
A common scenario I run into is looping over element list returned from document.querySelectorAll. Instead of returning an array of matched elements, it returns a node list.
A node list is not an array, but an enumeration object, containing each of the matched elements. This means the Array.forEach method is not available.
The node list object does have a forEach method that functions the exact same as the Array forEach method. Which is nice, but just note you are not working against an array, so if you supply an enumeration object to a method expecting an array things might blow up on you.
You can still iterate over the list using a traditional for loop or accessing an element directly using array index notation, elements[index].
There is a NodeList forEach polyfil you might consider loading if you still have legacy browsers to support.
forEach Performance Hit
If you are concerned with performance, like I am, then forEach may not be the best choice.
A developer created a simple test using jsPerf.com demonstrating the forEach method is about 95% slower than using the traditional for loop.
I will point out the tests on jsPerf may or may not reflect real performance because the test cases are very sterile. You should always consider real context when considering performance. The test case handled a simple scenario.
What your callback method actually does will affect the performance of course. And forEach is an abstraction and they almost always add overhead.
This makes sense if you understand how convenience methods like forEach are created. Newer, ES6 vintage syntax and convenience methods like forEach are added to the JavaScript engine (V8, Chakra, etc.) using a 'polyfil' that abstracts the lower level way of achieving the same thing, the for loop in this case.
So in essence you are calling a function that creates the traditional for loop behind the scenes.
Breaking a forEach Method
You should also consider you cannot break the forEach method as you can with the basic for loop.
I often loop over an array to find an element that matches criteria. In a classic for loop you can set the index to the array length to stop execution. You can also use a break statement to stop the for loop.
This can't be done with forEach.
Instead you may want to think about using the array filter as an alternative. It will return items where the callback method returns true. But it will also iterate over all the items, even if you have the right element(s) already.
There are scenarios where forEach adds value. For instance, when you want to create a new variable within the loop, forEach creates a new instance, where the for loop will assign a new value.
This has tripped me up several times over the years.
My advice is to think about when you use forEach and avoid using it because it feels more convenient. Use it when it provides value.
But a utility method might be a better way.
Lodash forEach() to Iterate over Objects
Never fear, Lodash is here!
Lodash is a JavaScript utility library that can reduce your code size and quality through its many functions. Lodash has a helpful iteration methods, such as forEach and map that work on objects as well as arrays.
_.forEach(arr, function(element, i) {
console.log(element);
});
forEach is included in the Lodash Core build, a small (4kb) size library that includes the most common methods used. The forEach method is also aliased to the each method.
This is great when you want to loop through the list in order. But what do you do when you want to loop from the bottom of the list, in reverse order?
Lodash has a sibling method, forEachRight. This method starts at the end or right of the list and loops in reverse order.
_.forEachRight([1, 2], function(value) {
console.log(value);
});// => Logs 2
then 1
.
I like Lodash, especially when writing node modules. It has so many useful methods and its documentation is a progressive web app!
The Old School jQuery each() Method
jQuery, like Lodash, includes a generic iterator method, each. You should note, there is no jQuery forEach method, which I think many developers expect. The jQuery each method has two parameters, an array and a callback.
The callback method has two values passed to it, the current index and the item value, which is the opposite of the array and Lodash forEach methods. The callback method is executed against each item in the array.
$.each(myArray, function( index, value ) {
console.log( index + ": " + value );
});
If you pass an object instead of an array the function loops over the object's properties, using the name as a key.
This makes sense if you understand each JavaScript object is an associative array. When you think about a JavaScript in terms of an associative array the index is the member name.
obj["property-name"]
This returns a reference to the value, which could be a traditional value, function, array or a child object.
I love associative arrays because they make JavaScript super flexible. I can't tell you how many projects I have needed an array I can quickly reference by a name, not a numeric index. These have come in handy many times.
The jQuery each method makes it easy to iterate over objects using the associative syntax.
var obj = { "flammable": "inflammable", "duh": "no duh"};
$.each( obj, function( key, value ) { alert( key + ": " + value );});
But you don't need jQuery to use this technique!
Iterating Over a JavaScript Associative Array
I used this technique in my Single Page App Router. When I composed my SPA's routes I would use the route slug as the index and the value the view's object.
I remember reading about Netflix dealing with memory leaks in the Express routing engine several years ago. The problem they encountered was due to the route engine using traditional arrays to manage routes.
They experienced memory leaks because they update the site hundreds if not thousands of times a day. Each time an update was published the updated page data was added to the express route table.
Unfortunately, due to it being a simple array duplicate routes were added. The amount of overhead to keep the route table clean was heavy.
This story caused me to review how I handled my SPA routes. Fortunately, I could not have this problem because I was using associative arrays.
if (routes.hasOwnProperty(slug)) {
return routes[slug];
}
for (key in routes) {
if (routes.hasOwnProperty(key)) {
route = routes[key]; //do more here
}
}
This is just one example of how I have used JavaScript object properties as an array index. I have found this technique useful in many situations.
Bonus: It is not a god idea to iterate over an array using for for ... in loop. That's because this expression iterates over the enumerable properties. So for an array you wont get values, you will get the item index.
Summary
Looping over arrays is a fundamental language construct. JavaScript has several native ways to iterate an array. Utility libraries like Lodash and jQuery also have helpful methods to make traversing array and object values easy to manage.
Your challenge to is find the technique that works best for your situation.