OK to delete hash pairs while iterating through it?

J

Jerry Krinock

In at least one other language, I read that it is not safe to delete
array or dictionary elements while iterating through it. Obviously,
this could cause trouble. Is this safe to do with a hash in perl?

Example: The following sub works OK, but is it guaranteed safe?

sub removeEmptyStringValuesFromHashRef {
my $hashRef = shift ;
while (my ($key, $value) = each(%$hashRef)) {
if (defined($value)) {
if (length($value) == 0) {
delete ($hashRef->{$key}) ;
}
}
}
}
 
P

Peter Makholm

Jerry Krinock said:
In at least one other language, I read that it is not safe to delete
array or dictionary elements while iterating through it. Obviously,
this could cause trouble. Is this safe to do with a hash in perl?

The documentation for the each function says:

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. Exception:
It is always safe to delete the item most recently returned by
"each()", which means that the following code will work:

[code example snipped]

The exception should answer you question.

//Makholm
 
J

Joost Diepenmaat

Jerry Krinock said:
In at least one other language, I read that it is not safe to delete
array or dictionary elements while iterating through it. Obviously,
this could cause trouble. Is this safe to do with a hash in perl?

Example: The following sub works OK, but is it guaranteed safe?

sub removeEmptyStringValuesFromHashRef {
my $hashRef = shift ;
while (my ($key, $value) = each(%$hashRef)) {
if (defined($value)) {
if (length($value) == 0) {
delete ($hashRef->{$key}) ;
}
}
}
}

from "perldoc -f each":

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. Exception: It is
always safe to delete the item most recently returned by "each()",
which means that the following code will work:

while (($key, $value) = each %hash) {
print $key, "\n";
delete $hash{$key}; # This is safe
}
 
X

xhoster

Peter Makholm said:
Jerry Krinock said:
In at least one other language, I read that it is not safe to delete
array or dictionary elements while iterating through it. Obviously,
this could cause trouble. Is this safe to do with a hash in perl?

The documentation for the each function says:

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. Exception:
It is always safe to delete the item most recently returned by
"each()", which means that the following code will work:

[code example snipped]

The exception should answer you question.

From a previous discussion here, it seems the documentation is wrong, but
not in a way that affects the OP. It is safe to delete any item, including
the one just returned.

Making it safe to delete the one just returned required a special case in
the hash code guts, while deleting anything else is "naturally" safe.

Adding, on the other hand, is something best avoided.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
E

Eric Pozharski

Making it safe to delete the one just returned required a special case
in the hash code guts, while deleting anything else is "naturally"
safe. Adding, on the other hand, is something best avoided.

(Just opening one more DGBI war)

First I picked that:

perl -wle '
%x = qw(a b c d e f);
$z = 0;
while($y = each %x) {
print "$y => $x{$y}";
$x{$z++} = $z++ if $z < 10; }
'
e => f
1 => 0
c => d
a => b
3 => 2
7 => 6
9 => 8
5 => 4

I don't know what's going on here. The second, I believe, should be
more clear:

perl -wle '
%x = qw(a b c d e f);
%y = qw(z y x w v u);
while($z = each %x) {
print "$z => $x{$z}";
$z = each %y;
defined $z or next;
$x{$z} = $y{$z}; }
'
e => f
c => d
a => b
x => w
v => u
z => y

What surprises me most, is key-messing in first case, which doesn't show
in second.
 
X

xhoster

Eric Pozharski said:
(Just opening one more DGBI war)

What's a DGBI war?
First I picked that:

perl -wle '
%x = qw(a b c d e f);
$z = 0;
while($y = each %x) {
print "$y => $x{$y}";
$x{$z++} = $z++ if $z < 10; }
'
e => f
1 => 0
c => d
a => b
3 => 2
7 => 6
9 => 8
5 => 4

I don't know what's going on here.

I don't know what part you don't know. Could you be more specific.

The second, I believe, should be
more clear:

And I don't *what* you think will be made clear.
perl -wle '
%x = qw(a b c d e f);
%y = qw(z y x w v u);
while($z = each %x) {
print "$z => $x{$z}";
$z = each %y;
defined $z or next;
$x{$z} = $y{$z}; }

Here, the value of $x{$z} is changed, but no *keys* are added or deleted
from %x while it is being iterated over by each.

'
e => f
c => d
a => b
x => w
v => u
z => y

What surprises me most, is key-messing in first case, which doesn't show
in second.

Which key do you consider to be missing? Adding to a hash can give
"weird" results, but your first example doesn't illustrate that in any way.

Are you confusing yourself with the increment operator and order of
execution? Sorry, I just don't see what you are seeing in that data.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
X

xhoster

Here, the value of $x{$z} is changed, but no *keys* are added or deleted
from %x while it is being iterated over by each.

No, I misinterpreted that. Yes, keys are being added to %x.


But what conclusion are you drawing from this?

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
E

Eric Pozharski

But what conclusion are you drawing from this?

The first conclusion is that someone makes too much noise. The second
is that someone has to use bigger samples if he wants meaningful
results. That (below) clearly shows that adding keys while iterating
hash is unsafe.

perl -wle '
foreach(0 .. 25) {
$x{sprintf q|x%02i|, $_} = 1;
$y{sprintf q|y%02i|, $_} = 1; };
print scalar %x;
print scalar %y;
$m = 0;
while($z = each %x) {
print "$z => ", $m++;
$z = each %y;
defined $z or next;
$x{$z} = $y{$z}; }
' |sort
18/32
19/32
x00 => 30
x01 => 1
x02 => 0
x02 => 20
x03 => 9
x04 => 37
x05 => 12
x06 => 27
x07 => 5
x08 => 16
x09 => 18
x10 => 8
x11 => 23
x12 => 33
x13 => 7
x14 => 42
x15 => 21
x15 => 3
x16 => 14
x17 => 36
x18 => 11
x19 => 2
x20 => 13
x21 => 19
x22 => 10
x23 => 15
x24 => 28
x25 => 17
y02 => 26
y03 => 29
y04 => 34
y06 => 38
y07 => 40
y09 => 31
y10 => 39
y11 => 24
y12 => 22
y13 => 6
y17 => 25
y18 => 35
y21 => 41
y22 => 43
y23 => 4
y24 => 32

p.s. Do bigger samples mean bigger noise?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top