2

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 ]};
                        }
                }
        }

}

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416

3 Answers3

3

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;
}
amon
  • 57,091
  • 2
  • 89
  • 149
  • Thanks! Is it okay to omit `@keys` in your code and just use `for my $k (keys %hash)` ? – Alexander Farber Jul 10 '13 at 13:41
  • 1
    @AlexanderFarber Yes, in this case the `@keys` can be omitted. I used an extra variable to make the copying of the key list clear. However, one cannot use `foreach` loops if one would modify the array that is being iterated. – amon Jul 10 '13 at 13:47
2

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."

Miguel Prz
  • 13,718
  • 29
  • 42
0

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.

Dave Cross
  • 68,119
  • 3
  • 51
  • 97