Last night a friend passed along a short problem to me. I need to take the keys for a multi-level hash, say $hash{a}{b}{c}{d}, and create that structure from just the list qw(a b c d), without using eval or a module, and it had to be really short, and it had to be delivered right away
After my initial solution, I came up with this concept (the argument checking and so on to be done by someone else). I use two hash references. One references the top level of the hash I want to create, and the other one is something like a cursor inside that hash so I can move around in it.
sub set_multi_level_hash
{
my( $hash, $tiers, $value ) = @_;
my $work_hash = $hash;
foreach my $tier ( @$tiers )
{
$work_hash->{ $tier } = {} unless exists $work_hash->{ $tier };
$work_hash = $work_hash->{ $tier };
}
$work_hash = _get_multi_hash_last_level( $hash, $tiers );
$work_hash->{ $tiers->[-1] } = $value;
}
I do not really like that part where I have to back up a bit to figure out where to assign the value. I am sure there is a better way to do this, but I got the problem at 1 am and I needed to deliver it right away.
The _get_multi_hash_last_level routine uses the same trick as the earlier routine, but I do not have to remember anything about the original hash. I just need to drill-down to the right level.
sub _get_multi_hash_last_level
{
my( $hash, $tiers ) = @_;
foreach my $tier ( @{$tiers}[0..$#$tiers-1] )
{
$hash = $hash->{ $tier };
}
$hash;
}
That may look short, but it was longer before because I had made a mistake that required me to handle two special cases. The original version worked, but before I was able to become unconscious in bed, I came up with that version.
Somewhere else, somebody else created the code to read the multi-level hash under the same restrictions, I think, but I did not get to see that code. I wonder what it does to get to the right place.
make use of the indices of the $tiers (Score:1)
use strict;
use warnings;
use Data::Dumper;
my $hash = {};
set_multi_level_hash($hash, [qw/ a b c d
print Dumper $hash;
sub set_multi_level_hash
{
my ( $hash, $tiers, $value ) = @_;
for (0
{
$hash->{$tiers->[$_]} ||= ( $_ == $#$t
Re:make use of the indices of the $tiers (Score:1)
$hash->{$tiers->[$_]} = {} unless exists $hash->{$tiers->[$_]};
$hash->{$tiers->[$_]} = $value if $_ == $#$tiers;
Re:make use of the indices of the $tiers (Score:2)
Oh well, too late now. It is out of my hands.
Thanks for the code
Re:make use of the indices of the $tiers (Score:2)
set_multi_level_hash($h, [qw(a b c)], 42);
set_multi_level_hash($h, [qw(a b)], 66);
Oops, we just nuked the first value and the whole "subhash".
In other words, is the level of multiness going to stay the same?
Personally, I would either create a class that guards against the multiness changing, or would make the data structure to be an array reference of hash references, keyed off in the array reference on the depth of $tiers (the latter can of course
Re:make use of the indices of the $tiers (Score:2)
I suppose you could create a class to limit operations, but then, you probably would have need ed a lot more time to deliver the code. One of the requirements was "right now". I didn't build in a lot of extra stuff to make variables not be variable.
There is no get_multi_level_hash functio
Got a similar problem (Score:2)
I had to do someting similar to create a multilevel hash table that returns a list of "cached" things given a list of parameters that represent the hashing value.
The main problem was not creating the various anonymous hashes, but that the last item is not a {} but a []. The trick was to keep a reference to the previous step, to be able to go back and turn a newly created hash reference into an anonymous array.