Eily has asked for the wisdom of the Perl Monks concerning the following question:
Again, some of you might have seen the question on the CB already. (And again, I came up with a solution just because asking made me see things clear enough). I am wondering about iterating over an hash while removing some of its keys (the current one, and several connected ones). Actually, to explain my problem on the CB I said I'd like to be able to do this:
where first does the same as each, but with a new iterator (ie, from the start). Long story short, this led to this:while (my ($k,$v) = first %hash) { delete $hash{$_} for $k, @$v; }
use strict; use warnings; use feature 'say'; use Data::Dump qw( pp ); sub first (\%) { my $hash = shift; scalar(keys %$hash); # Force reset each %$hash; } my %hash = ( 1 => [2..7], 8 => [ 9 ], 9 => [ 8 ], (map { $_ => [ 1 ] } 2..7), ); say "Input hash:", pp \%hash; say ""; say "Try first then each twice in a row to check that the iteration is + restarted"; say pp [first %hash], [each %hash] for 1..2; say ""; # Delete a key and all the keys contained in the value array sub rec_delete(\%$); sub rec_delete(\%$) { my ($hash, $key) = @_; my $array = delete $hash->{$key}; return unless $array; say " Removing $key -> [ @$array ]"; rec_delete %$hash => $_ for @$array; } while (my ($k,$v) = first %hash) { say "Starting from $k -> [ @$v ]"; rec_delete %hash => $k; }
This works just fine. But I'm curious about other ways to do the same (because this seems like it could be a normal enough use of hashes?). So the exercise is:Input hash:{ 1 => [2 .. 7], 2 => [1], 3 => [1], 4 => [1], 5 => [1], 6 +=> [1], 7 => [1], 8 => [9], 9 => [8] } Try first then each twice in a row to check that the iteration is rest +arted ([8, [9]], [5, [1]]) ([8, [9]], [5, [1]]) Starting from 8 -> [ 9 ] Removing 8 -> [ 9 ] Removing 9 -> [ 8 ] Starting from 5 -> [ 1 ] Removing 5 -> [ 1 ] Removing 1 -> [ 2 3 4 5 6 7 ] Removing 2 -> [ 1 ] Removing 3 -> [ 1 ] Removing 4 -> [ 1 ] Removing 6 -> [ 1 ] Removing 7 -> [ 1 ]
Without expanding the whole list of keys (let's pretend there's an awful lot of them, or the hash is tied, and it's harder to find an existing key than fetch a value). So @keys = keys %hash; for my $key (@keys) { recursive_delete(\%hash, $key); } isn't valid.While the hash isn't empty Take one key (at random) from the hash Delete that key and all connected keys
NB: the output of my example is actually random, so you might get a different cycle by trying it.
|
---|
Back to
Seekers of Perl Wisdom