If I have a Perl hash with a bunch of (key, value) pairs, what is the preferred method of iterating through all the keys? I have heard that using each
may in some way have unintended side effects. So, is that true, and is one of the two following methods best, or is there a better way?
# Method 1
while (my ($key, $value) = each(%hash)) {
# Something
}
# Method 2
foreach my $key (keys(%hash)) {
# Something
}
Using the each syntax will prevent the entire set of keys from being generated at once. This can be important if you're using a tie-ed hash to a database with millions of rows. You don't want to generate the entire list of keys all at once and exhaust your physical memory. In this case each serves as an iterator whereas keys actually generates the entire array before the loop starts.
So, the only place "each" is of real use is when the hash is very large (compared to the memory available). That is only likely to happen when the hash itself doesn't live in memory itself unless you're programming a handheld data collection device or something with small memory.
If memory is not an issue, usually the map or keys paradigm is the more prevelant and easier to read paradigm.
The rule of thumb is to use the function most suited to your needs.
If you just want the keys and do not plan to ever read any of the values, use keys():
If you just want the values, use values():
If you need the keys and the values, use each():
If you plan to change the keys of the hash in any way except for deleting the current key during the iteration, then you must not use each(). For example, this code to create a new set of uppercase keys with doubled values works fine using keys():
producing the expected resulting hash:
But using each() to do the same thing:
produces incorrect results in hard-to-predict ways. For example:
This, however, is safe:
All of this is described in the perl documentation:
I usually use
keys
and I can't think of the last time I used or read a use ofeach
.Don't forget about
map
, depending on what you're doing in the loop!A few miscellaneous thoughts on this topic:
values
returns aliases which means that modifying them will modify the contents of the hash. This is by design but may not be what you want in some circumstances.each
. This is not true forkeys
aseach
is an iterator whilekeys
returns a list.I woudl say:
This give 2 major advantages:
I don't think it's more expensive to use keys over each, so no need for two different constructs for the same thing in your code.
One thing you should be aware of when using
each
is that it has the side effect of adding "state" to your hash (the hash has to remember what the "next" key is). When using code like the snippets posted above, which iterate over the whole hash in one go, this is usually not a problem. However, you will run into hard to track down problems (I speak from experience ;), when usingeach
together with statements likelast
orreturn
to exit from thewhile ... each
loop before you have processed all keys.In this case, the hash will remember which keys it has already returned, and when you use
each
on it the next time (maybe in a totaly unrelated piece of code), it will continue at this position.Example:
This prints:
What happened to keys "bar" and baz"? They're still there, but the second
each
starts where the first one left off, and stops when it reaches the end of the hash, so we never see them in the second loop.