29

I have stumbled into several methods of looping in JavaScript, what I like the most is:

for(var i = 0; i < a.length; i++){
    var element = a[i];
}

But as tested here (http://www.robertnyman.com/2008/04/11/javascript-loop-performance/), it should probably be written so that the length is only calculated once.

In jQuery there is a .each that you can stick a function. I like this a little better, because I don't have to type the array twice, like in the above solution.

If JavaScript supported macros it would be a piece of cake to roll your own, but sadly it does not.

So what do you guys use?

Jeff Atwood
  • 63,320
  • 48
  • 150
  • 153
Anders Rune Jensen
  • 3,758
  • 2
  • 42
  • 53
  • Apparently what you mean by "best" is "cleanest," right? Some might say the "best" way is to do it whichever way is quickest, in which case the answer is probably different... – Jason Bunting Oct 11 '08 at 01:51
  • Yeah well. I picked the word best, because I wanted one looping solution that I can use always without really thinking about it. So it must not be horribly slow when running on 100k elements, it must not be horribly long to write etc. – Anders Rune Jensen Oct 11 '08 at 04:23

9 Answers9

37

I've started using iterators where relevant. Performance is reasonable, however more importantly it allows you to encapsulate the looping logic:

function createIterator(x) {
    var i = 0;

     return function(){
       return x[i++];
    };
}

Then to use:

var iterator=createIterator(['a','b','c','d','e','f','g']);

iterator();

returns "a";

iterator();

returns "b";

and so on.

To iterate the whole list and display each item:

var current;

while(current=iterator())
{
    console.log(current);
}

Be aware that the above is only acceptable for iterating a list that contains "non-falsy" values. If this array contained any of:

  • 0
  • false
  • ""
  • null
  • NaN

the previous loop would stop at that item, not always what you want/expect.

To avoid this use:

var current;

while((current=iterator())!==undefined)
{
   console.log(current);
}
Ash
  • 60,973
  • 31
  • 151
  • 169
  • Yeah, I have also been thinking about using iterators. They much better encapsulate the concept of traversing something than simple loops do. But how would you in your example print all elements in the iterator? – Anders Rune Jensen Oct 11 '08 at 01:39
  • Beautiful closure sweetness. Ahh, I loves 'em. – Jason Bunting Oct 11 '08 at 01:51
  • How do you know when you've reached the end if your iterator doesn't have a HasNext function? If you just keep on calling "iterator" per your example, you will eventually get array index out of bounds. – Kibbee Oct 11 '08 at 01:54
  • Kibbee, Anders, I've added a simple example of how to iterate the whole list to the answer. – Ash Oct 11 '08 at 01:57
  • 2
    Sorry, I guess i'm a little to conditioned to using languages that throw exceptions when you try to access past the end of the array to try to code something that relies on the fact that JS doesn't throw an exception when you try to do this. – Kibbee Oct 11 '08 at 02:14
  • Kibbee, No need to be sorry, I should have added that example earlier. One of the most important things to know in Javascript is the "falsy" values (values that are evaluated as false). These are: "", null, 0, NaN, false and finally undefined. The iterator relies on undefined being returned. – Ash Oct 11 '08 at 02:23
  • 1
    If you liked this answer, you may be interested in http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/ and Mochikit's http://mochikit.com/doc/html/MochiKit/Iter.html – Jonny Buchanan Oct 11 '08 at 04:13
  • This is rather hackish. Me like it! – Manuel Ferreria Jan 07 '09 at 19:59
  • Welcome to the world of Slow and Confusing. This idiom is *far* from common in JavaScript world, so people will have difficulties with your codebase if you decide to use this. If you have large loops, this approach is also *very* slow. Function calls are expensive in JS. – Jani Hartikainen Jan 21 '10 at 16:40
  • @Jani, welcome to the world of New Ideas! This answer might help make the idiom more common. Also, performance is more than acceptable for normal day to day usage in my experience, and this depends heavily on the Javascript engine running it anyway. Function calls may be expensive, but un-maintainable Javascript costs a hell of a lot more! – Ash Jan 21 '10 at 17:01
  • A "standard" loop is hardly unmaintainable. However I can see you understood my point ;) – Jani Hartikainen Jan 21 '10 at 20:05
  • Love the sexy use of closures, although I hardly believe this is "the best way" to loop an array in Javascript. A standard for loop with a cached length is still the fastest, most flexible way IMHO. But for normal everyday shit this should be fine, nice work ;) – mynameistechno Aug 26 '11 at 23:35
10

Small improvement to the original, to only calculate the array size once:

for(var i = 0, len = a.length; i < len; i++){ var element = a[i]; }

Also, I see a lot of for..in loops. Though keep in mind that it's not technically kosher, and will cause problems with Prototype specifically:

for (i in a) { var element = a[i]; }
Chase Seibert
  • 15,703
  • 8
  • 51
  • 58
  • 2
    for..in loops are used to iterate over object properties, while they seem to work for Arrays, they will also iterate over the 'length' property or any other dynamically added property. That's why it does not work well with Prototype. – Vincent Robert Oct 12 '08 at 16:02
  • 1
    the second example `i` is probably creating a global – ajax333221 May 23 '12 at 23:58
6

Just store the length in a variable first.

  var len = a.length;
  for (var i = 0; i < len; i++) {
    var element = a[i];
  }
Randy Sugianto 'Yuku'
  • 71,383
  • 57
  • 178
  • 228
6

I know I'm late to the party, but I use reverse loops for loops that don't depend on the order.

Very similar to @Mr. Muskrat's - but simplifying the test:

var i = a.length, element = null;
while (i--) {
  element = a[i];
}
Remy Sharp
  • 4,520
  • 3
  • 23
  • 40
  • well I'm *very* late to the party, but this is the correct answer and should be accepted as such. For the uninitiated, the i-- clause saves a comparison (because 0 = false in JS tests). Caveat 1: reverse order! Caveat 2: readability isn't great. Caveat 3: a cached for loop is very nearly as good – annakata Jan 07 '09 at 20:45
2

You could just always use a while loop, and compute the array limit before hand.

Var max = a.length-1;
var i = 0;

while(i <= max)
{
var element = a[i];
i++;
}
Kibbee
  • 65,369
  • 27
  • 142
  • 182
  • Sorry to be blunt, but I fail to see how this is an improvement. You still specify the array twice, and in your solution the environment around is polluted with two variables which scrope should not transcend the while loop construct. – Anders Rune Jensen Oct 11 '08 at 01:29
  • The environment is going to be "polluted" anyway, given that in Javascript the scope of a variable is the enclosing function, not the block in which it is declared. – Jason Etheridge Oct 11 '08 at 01:41
  • Testing it against the link given in the question, it's at least as fast, if not faster, than any of the for loop implementations. – Kibbee Oct 11 '08 at 02:06
1

If you have many elements in the array and speed is an issue then you want to use a while loop that iterates from highest to lowest.

  var i = a.length;
  while( --i >= 0 ) {
    var element = a[i];
    // do stuff with element
  }  
Mr. Muskrat
  • 22,772
  • 3
  • 20
  • 21
1

I don't use it myself, but one of my colleagues uses this style:

var myArray = [1,2,3,4];
for (var i = 0, item; item = myArray[i]; ++i) {
    alert(item);
}

like Ash's answer, this will hit issues if you've got "falsey" values in your array. To avoid that problem change it to (item = myArray[i]) != undefined

nickf
  • 537,072
  • 198
  • 649
  • 721
0

I don't see what the problem with using a standard for(;;) loop is. A little test

var x;
var a = [];
// filling array
var t0 = new Date().getTime();
for( var i = 0; i < 100000; i++ ) {
    a[i] = Math.floor( Math.random()*100000 );
}

// normal loop
var t1 = new Date().getTime();
for( var i = 0; i < 100000; i++ ) {
    x = a[i];
}

// using length
var t2 = new Date().getTime();
for( var i = 0; i < a.length; i++ ) {
    x = a[i];
}

// storing length (pollution - we now have a global l as well as an i )
var t3 = new Date().getTime();
for( var i = 0, l = a.length; i < l; i++ ) {
    x = a[i];
}

// for in
var t4 = new Date().getTime();
for( var i in a ) {
    x = a[i];
}

// checked for in
var t5 = new Date().getTime();
for( var i in a ) {
    if (a.hasOwnProperty(i)) {
        x = a[i];
    }
}

var t6 = new Date().getTime();
var msg = 'filling array: '+(t1-t0)+'ms\n'+
          'normal loop: '+(t2-t1)+'ms\n'+
          'using length: '+(t3-t2)+'ms\n'+
          'storing length: '+(t4-t3)+'ms\n'+
          'for in: '+(t5-t4)+'ms\n'+
          'checked for in: '+(t6-t5)+'ms';
console.log( msg );

results in:

filling array: 227ms
normal loop: 21ms
using length: 26ms
storing length: 24ms 
for in: 154ms
checked for in: 176ms

So:- for in's take the longest, using the length property (which is a property and doesn't need to be calculated) is nearly as fast as storing it first - which is only a whisker slower than using an integer.
AND a for() is the usual way to loop over an array, which everyone expects and understands.

All of them add a variable to the scope they run in - i - which is a common name for this use and so shouldn't be used for other things. Storing the length first adds another var - l - to the scope, which is unnecesary

meouw
  • 41,754
  • 10
  • 52
  • 69
-2

So, first you identify the perfect javascript loop, I believe it should look like this:

ary.each(function() {$arguments[0]).remove();})

This may require the prototype.js library.

Next, you get disgustet with the arguments[0] part and have the code be produced automatically from your server framework. This works only if the ladder is Seaside.

Now, you have the above generated by:

ary do: [:each | each element remove].

This comes complete with syntax completion and translates exactly to the above javascript. And it will make people's head spin that haven't used seasides prototype integration before, as they read your code. It sure makes you feel cool, too. Not to mention the gain in geekiness you can get here. The girls love it!

nes1983
  • 15,209
  • 4
  • 44
  • 64