views:

1294

answers:

12

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?

+2  A: 

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
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
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
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
+30  A: 

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
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
Beautiful closure sweetness. Ahh, I loves 'em.
Jason Bunting
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
Kibbee, Anders, I've added a simple example of how to iterate the whole list to the answer.
Ash
Love it. Thanks!
Anders Rune Jensen
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
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
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
insin
This is rather hackish. Me like it!
Manuel Ferreria
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
@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
A "standard" loop is hardly unmaintainable. However I can see you understood my point ;)
Jani Hartikainen
+8  A: 

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
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
+6  A: 

Just store the length in a variable first.

  var len = a.length;
  for (var i = 0; i < len; i++) {
    var element = a[i];
  }
yuku
+1  A: 

And you can see a little test on the issue in: http://stackoverflow.com/questions/157260/whats-the-best-way-to-loop-through-a-set-of-elements-in-javascript#161664

roenving
+1  A: 

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
the >= 0 isn't needed, just change --i to i--
Paul Hargreaves
A: 

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
+4  A: 

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
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
A: 

I tried Ash's solution and sadly declaring the two variables is quite tiresome in the end. I'm currently using the .each() from jquery and so far it's the best I have found. The only problem is that one can't return directly from the loop, and that break and continue is implementated as ugly idiosyncrasies (return true/false).

Anders Rune Jensen
+1  A: 

http://blogs.sun.com/greimer/entry/best_way_to_code_a

That basically covers the whole subject

pretty wierd that the "while (i--)" solution is the fastest ;-)
Anders Rune Jensen
A: 

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
A: 

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