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 }
Loop over Perl hash values Perl allows to Loop over its Hash values. It means the hash is iterative type and one can iterate over its keys and values using 'for' loop and 'while' loop. In Perl, hash data structure is provided by the keys() function similar to the one present in Python programming language.
We can done this by using map function. map {print "$_\n"} keys %hash; map function process its statement for every keys in the hash. The map function is there to transform a list.
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():
foreach my $key (keys %hash) { ... }
If you just want the values, use values():
foreach my $val (values %hash) { ... }
If you need the keys and the values, use each():
keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop while(my($k, $v) = each %hash) { ... }
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():
%h = (a => 1, b => 2); foreach my $k (keys %h) { $h{uc $k} = $h{$k} * 2; }
producing the expected resulting hash:
(a => 1, A => 2, b => 2, B => 4)
But using each() to do the same thing:
%h = (a => 1, b => 2); keys %h; while(my($k, $v) = each %h) { $h{uc $k} = $h{$k} * 2; # BAD IDEA! }
produces incorrect results in hard-to-predict ways. For example:
(a => 1, A => 2, b => 2, B => 8)
This, however, is safe:
keys %h; while(my($k, $v) = each %h) { if(...) { delete $h{$k}; # This is safe } }
All of this is described in the perl documentation:
% perldoc -f keys % perldoc -f each
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 using each
together with statements like last
or return
to exit from the while ... 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:
my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 ); # find key 'baz' while ( my ($k, $v) = each %hash ) { print "found key $k\n"; last if $k eq 'baz'; # found it! } # later ... print "the hash contains:\n"; # iterate over all keys: while ( my ($k, $v) = each %hash ) { print "$k => $v\n"; }
This prints:
found key bar found key baz the hash contains: quux => 4 foo => 1
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With