JavaScript lets you iterate arrays using the for each…in statement:
for each (var item in [1, 2, 3]) alert(item);
JavaScript 1.6 added the Array.forEach method:
[1, 2, 3].forEach(function(item) { alert(item) });
I’ve always preferred Perl’s statement modifiers, though, for the popular English-like order of their clauses (“do this for each of those”):
print $_ foreach (1, 2, 3);
JavaScript 1.7 added array comprehensions for array initialization:
var squares = [item * item for each (item in [1, 2, 3])];
I just realized I can (ab)use comprehensions to iterate arrays with Perl-like syntax by throwing away the result:
[alert(item) for each (item in [1, 2, 3])];
I can iterate object properties the same way:
var obj = { foo: 1, bar: 2, baz: 3 };
[alert(name + "=" + obj[name]) for (name in obj)];
Sweet!
The MDC docs for “for each” say not to iterate arrays that way, too, so I never use it on them.
-Max
You can also use Iterators for the objects to get the key and val as an array:
[keyVal.join(” = “) for (keyVal in Iterator({a:1,b:2,c:3}))].join(“n”)
output:
a = 1
b = 2
c = 3
Or alternatively, destructure the keyVal with [key,val] in Iterator(..)
Max, the reason for that is because for (each) in iterates up the prototype as well. So you run the risk of shooting yourself in the foot if you do:
Array.prototype.func = function() 42;
[i for each (i in [1,2,3])]
returns
1,2,3,function () 42
which is a risk you avoid if you explicitly iterate from i to length.
Edward: great tip! With an Iterator and destructuring assignment, the example in my post is:
[alert(key + “=” + val) for ([key, val] in Iterator({a:1,b:2,c:3}))]
Max, Blake: hmm, I use for each… in on arrays all the time. I guess I don’t modify the Array prototype that often (or write code that runs in contexts in which other code modifies it).
Although .forEach avoids the prototype property trap, it has the disadvantage of creating a nested context so that “this” refers to the global scope.
Myk: One thing to remember is that for each is significantly slower than iterating over array indexes, last time I checked.
I like to use it but I avoid it in performance-sensitive places.
> .forEach .. disadvantage of creating a nested context so that "this" refers to the global scope
You can pass in a second argument to forEach which will set "this" for the callback.
arr.forEach(func, this)
There are other ways to bind the callback function to a given "this" too.
With ES5 (coming soon to a SpiderMonkey trunk near you) the array-prototype-modification-leakage problem will be greatly reduced, since library authors will be able to make non-enumerable properties.
If this pattern becomes common we can probably also teach SpiderMonkey to just transform that into a loop, since the result is thrown away. (And for dense arrays, which we can detect in a number of ways, we can probably optimize further to avoid any iterator overhead, and just make it work like a for loop with a single fetch of the length. Good times!)
It’s so interesting to see these tricks.
Blake said…
> the reason for that is because for (each)
> in iterates up the prototype as well.
One can also avoid the so called "prototype trap" by using the hasOwnProperty method.
Array.prototype.func1 = function() {}
var z = [1,2,3];
for (var i in z) {
if (z.hasOwnProperty(i)) {
document.write(z[i] + ", ");
}
}
prints 1, 2, 3,
does not print 1, 2, 3, function
boolean: very interesting! And since comprehensions can limit iteration to items that match a certain expression, you can actually use hasOwnProperty in a comprehension:
Array.prototype.foo = function() {};
var array = [1, 2, 3];
[alert(array[item]) for (item in array) if (array.hasOwnProperty(item))];
// alerts 1, 2, 3, not 1, 2, 3, function
Nevertheless, iterating Arrays as objects doesn’t guarantee order, if I remember correctly, and thus is not recommended.
Shortest
[1,2,3].map(alert);
Anonymous: indeed, although that doesn’t have the popular English-like order I like from Perl.
I guess I don't modify the Array prototype that often (or write code that runs in contexts in which other code modifies it).
Each avoids the prototype property trap, it has the disadvantage of creating a nested context so that "this" refers to the global scope.