Adding new members while iterating a hash with "each"

783 Views Asked by At

In perl 5.10.1 is it ok to add new members to a hash while iterating through it with the each operator?

Smth. like in this code (preparing data for Google charts), where I have a hash of hashes of arrays and I am trying to move the last element of each array:

sub split_aclr($) {
        my $csvData = shift;

        while (my ($csv, $href) = each %$csvData) {
                next unless $csv =~ /_ACLR_/i;
                my $len = scalar @{$href->{MEASUREMENT}};

                if ($len > 1 && $href->{MEASUREMENT}->[$len - 1] eq 'CARRIER_CHANNEL') {
                        while (my ($key, $arr) = each %$href) {
                                my $moved = pop @$arr;
                                $csvData{$csv . '_CARRIER_CHANNEL'} = {$key => [ $moved ]};
                        }
                }
        }

}

3

There are 3 best solutions below

2
amon On BEST ANSWER

It is not generally safe to add or remove entries of a hash while iterating it using each. If you want to do so, it might be best to save an explicit list of keys you want to iterate to an array, and loop over that.

Instead of

while (my $k, $v) = each %hash) {
  $hash{"foo_$k"} = $v;
}

do

my @keys = keys %hash;
for my $k (@keys) {
  my $v = $hash{$k};
  $hash{"foo_$k"} = $v;
}
0
Miguel Prz On

It isn't safe. If you do it, you will get a warning: "Use of each() on hash after insertion without resetting hash iterator results in undefined behavior."

0
Dave Cross On

The documentation for each() is pretty clear on this.

If you add or delete elements of a hash while you're iterating over it, you may get entries skipped or duplicated, so don't.