--------------------------------------------------------------------
4.57: What happens if I add or remove keys from a hash while iterating over it?
(contributed by brian d foy)
The easy answer is "Don't do that!"
If you iterate through the hash with each(), you can delete the key most
recently returned without worrying about it. If you delete or add other
keys, the iterator may skip or double up on them since perl may
rearrange the hash table. See the entry for "each()" in perlfunc.
--------------------------------------------------------------------
The perlfaq-workers, a group of volunteers, maintain the perlfaq. They
are not necessarily experts in every domain where Perl might show up,
so please include as much information as possible and relevant in any
corrections. The perlfaq-workers also don't have access to every
operating system or platform, so please include relevant details for
corrections to examples that do not work on particular platforms.
Working code is greatly appreciated.
If you'd like to help maintain the perlfaq, see the details in
perlfaq.pod.
I thought mjd'd fixed this entry?
> If you iterate through the hash with each(), you can delete the key most
> recently returned without worrying about it. If you delete or add other
> keys, the iterator may skip or double up on them since perl may
> rearrange the hash table. See the entry for "each()" in perlfunc.
Deleting *any* key is safe, and has the obvious result. If you've seen
it already, you won't see it again; if you haven't, you won't see it at
all. Deleting an entry never causes Perl hashes to rehash, so it won't
affect any other keys.
Adding a key is not safe. It may cause the hash to rehash, which will
change the order of all the keys.
Ben
BM> Quoth PerlFAQ Server <br...@stonehenge.com>:
>>
>> 4.57: What happens if I add or remove keys from a hash while iterating over it?
>>
>> (contributed by brian d foy)
>>
>> The easy answer is "Don't do that!"
BM> I thought mjd'd fixed this entry?
>> If you iterate through the hash with each(), you can delete the key most
>> recently returned without worrying about it. If you delete or add other
>> keys, the iterator may skip or double up on them since perl may
>> rearrange the hash table. See the entry for "each()" in perlfunc.
BM> Deleting *any* key is safe, and has the obvious result. If you've seen
BM> it already, you won't see it again; if you haven't, you won't see it at
BM> all. Deleting an entry never causes Perl hashes to rehash, so it won't
BM> affect any other keys.
BM> Adding a key is not safe. It may cause the hash to rehash, which will
BM> change the order of all the keys.
minor nit: it MAY change the order of all the keys. it could rehash the
keys to the same order but you can't expect that. your point of deleting
any key is valid. it would make no sense to rehash on that anyway. you
could be deleting a key from an overflow list in a bucket so why rehash
then?
so i agree this FAQ needs to be edited to clarify the safety of deleting
keys in a loop while emphasizing the error of adding keys in a loop.
uri
--
Uri Guttman ------ u...@stemsystems.com -------- http://www.sysarch.com --
----- Perl Code Review , Architecture, Development, Training, Support ------
--------- Free Perl Training --- http://perlhunter.com/college.html ---------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
> Quoth PerlFAQ Server <br...@stonehenge.com>:
> >
> > 4.57: What happens if I add or remove keys from a hash while iterating over
> > it?
> Deleting *any* key is safe, and has the obvious result. If you've seen
> it already, you won't see it again; if you haven't, you won't see it at
> all. Deleting an entry never causes Perl hashes to rehash, so it won't
> affect any other keys.
This is contrary to the documentation for each() in perlfunc. I'm
taking perlfunc as the more authoratative source. If there's a problem,
someone needs to fix it there first.
I think there *is* problem with F<perlfunc>
perl -wle '
$h{$_} = $_ foreach 0 .. 1E4;
print scalar %h;
while( $x = each %h ) {
delete $h{$x + 1};
push @z, $x + 1;
print "already deleted: $x"
if grep $_ == $x, @z;
};
print scalar %h;
'
7391/16384
3526/16384
--
Torvalds' goal for Linux is very simple: World Domination
Stallman's goal for GNU is even simpler: Freedom
EP> $h{$_} = $_ foreach 0 .. 1E4;
since i like slices:
%h{ 0 .. 1e4 } = 0 .. 1E4 ;
EP> while( $x = each %h ) {
EP>
EP> delete $h{$x + 1};
EP> push @z, $x + 1;
and very few seem to know that delete returns its value and since your
keys and values are the same:
push @z, delete $h{$x + 1} ;
:)
ITYM:
@h{ 0 .. 1e4 } = 0 .. 1E4 ;
> EP> while( $x = each %h ) {
> EP>
> EP> delete $h{$x + 1};
> EP> push @z, $x + 1;
>
> and very few seem to know that delete returns its value and since your
> keys and values are the same:
>
> push @z, delete $h{$x + 1} ;
>
> :)
John
--
Those people who think they know everything are a great
annoyance to those of us who do. -- Isaac Asimov
JWK> Uri Guttman wrote:
>>>>>>> "EP" == Eric Pozharski <why...@pozharski.name> writes:
>>
EP> $h{$_} = $_ foreach 0 .. 1E4;
>>
>> since i like slices:
>>
>> %h{ 0 .. 1e4 } = 0 .. 1E4 ;
JWK> ITYM:
JWK> @h{ 0 .. 1e4 } = 0 .. 1E4 ;
yep. i cut/pasted your code and forgot to change it.
Those who trash hashes know. I don't.
> EP> while( $x = each %h ) {
> EP>
> EP> delete $h{$x + 1};
> EP> push @z, $x + 1;
>
> and very few seem to know that delete returns its value and since your
> keys and values are the same:
>
> push @z, delete $h{$x + 1} ;
Those who B<delete> know. I don't.