Re: quick question about hash element access
by tilly (Archbishop) on Jun 27, 2003 at 21:18 UTC
|
Sorry, basically you have to do it recursively or using eval. For a miniscule efficiency gain you can use tail-optimization to turn the recursion into iteration like so:
sub nested_lookup {
my $node = shift;
while (ref($node) and @_) {
$node = $node->{ shift(@_) };
}
return @_ ? undef : $node;
}
and you would call it like this:
my $elem = nested_lookup(\%hash, split /\./, 'a.b.c.d');
(Note: could benefit from more error checking.) | [reply] [d/l] [select] |
|
sub nested : lvalue {
@_ == 1
? $_[0]
: nested($_[0]{$_[1]}, @_[2..$#_]);
}
..and now you can say:
nested(\%hash, a => b => c =>) = "VALUE";
TThis can lead to such fun looking constructs as nested $hashref, a => b => c => d => = 42; Note that this code will only work under perl 5.6.0 or later, though; earlier perls had more restricted definitions of lvalues. See also descending a tree of hash references.
perl -pe '"I lo*`+$^X$\"$]!$/"=~m%(.*)%s;$_=$1;y^`+*^e v^#$&V"+@( NO CARRIER' | [reply] [d/l] [select] |
Re: quick question about hash element access
by cLive ;-) (Prior) on Jun 27, 2003 at 22:52 UTC
|
Nice question :) I had a play using the lovely AUTOLOAD.
#!/usr/bin/perl
use strict;
use warnings;
our $AUTOLOAD;
our $hashref = {
'a' => {
'b' => {
'c' => 'this is c',
'd' => 'this is d'
}
}
};
print "a_b_c() = ".a_b_c()."\n";
print "a_b_d() = ".a_b_d()."\n";
exit(0);
sub AUTOLOAD {
my ($package,$ref_name) = split /::/, $AUTOLOAD;
my $ref = '$hashref'. join '', map { "->{$_}" } grep /^\w+$/, spli
+t /_/, $ref_name;
return eval $ref;
}
# output for above is:
a_b_c() = this is c
a_b_d() = this is d
.02
cLive ;-) | [reply] [d/l] |
Re: quick question about hash element access
by chromatic (Archbishop) on Jun 27, 2003 at 21:16 UTC
|
| [reply] |
Re: quick question about hash element access
by Nkuvu (Priest) on Jun 27, 2003 at 21:12 UTC
|
#!/usr/bin/perl -w
use strict;
my %test_hash;
$test_hash{'a'}{'b'}{'c'}{'d'} = "Yay";
print get_id("a.b.c.d"), "\n";
sub get_id
{
my @parts = split /\./, $_[0];
# Currently written to access a global hash
return $test_hash{$parts[0]}{$parts[1]}{$parts[2]}{$parts[3]};
}
I'm sure other monks can find more efficient (or bulletproof) ways. But this seems to work -- you could probably easily alter it so that it accepts a reference to a particular hash and uses that inside the sub.
Also note that $hash{'a'}{'b'}{'c'} is the same as $hash{'a'}->{'b'}->{'c'} | [reply] [d/l] [select] |
|
I suspect the question was not intended to be limited to hash paths of a specific depth. I'd suggest something like this:
sub hpath(\%$) {
my ($href, $path) = @_;
while ($path =~ /\G(.+?)\.?/gc) {
$href = $href->{$1};
}
return $href;
}
my %c = (1 => { 2 => { 3 => 4 }});
print hpath(%c, "1.2.3");
Only with error checking, of course. Substitute while ... with foreach (split(/\./, $path) (and $1 with $_) for slightly different flavor. | [reply] [d/l] [select] |
|
(For a another flavor altogether, try reduce:
use List::Util qw(reduce);
my %c = (1 => { 2 => { 3 => 4 }});
print reduce { $a->{$b} } \%c, split(/\./, "1.2.3");
) | [reply] [d/l] [select] |
|
added to your code so it can take a variable length identifier, and accept the hashref.
my %test_hash;
$test_hash{'a'}{'b'}{'c'}{'d'} = "Yay";
print get_id(\%test_hash, "a.b.c.d"), "\n";
sub get_id
{
my $thingy = shift;
my @keys = split /\./, shift;
foreach my $key ( @keys )
{
$thingy = $$thingy{$key};
}
return $thingy;
}
| [reply] [d/l] |
|
Interesting. But I have a question (not being totally fluent in references).
What exactly does the $thingy = $$thingy{$key} line do? My guess is that it assigns a scalar value referred to by the reference which is contained in $thingy{$key}. But I'm not even sure that sentence makes sense, much less if it's accurate. :)
| [reply] [d/l] [select] |
|
|
|
| [reply] |
|
| [reply] |
Re: quick question about hash element access
by jdklueber (Beadle) on Jun 28, 2003 at 00:23 UTC
|
This does what I think you're looking for. One caveat: It returns ANYTHING it finds at the end of the argument- even a hash ref (as though you hadn't gotten to the actual terminus). So if that's not what you're intending, you'll need to check your output.
use strict;
my %variable_hash;
$variable_hash{a}{b}{c}{d} = 40;
$variable_hash{e}{f}{g}{h}{i} = 50;
$variable_hash{a}{b}{c}{j} = 45;
sub get_value
{
my $argument = shift;
my @tmp = split /\./, $argument;
my %lastref = %variable_hash;
my $max = @tmp;
my $current = 0;
my $found = 0;
foreach my $arg (@tmp)
{
$current++;
last unless (exists($lastref{$arg}));
if ($current == $max)
{
return $lastref{$arg};
}
%lastref = %{$lastref{$arg}};
}
undef %lastref;
return %lastref;
}
print get_value("a.b.c.d");
print get_value("e.f.g.h.i");
print get_value("a.b.c.j");
print get_value("a.b.c.k");
print get_value("a");
--
Jason Klueber
ookami@insightbb.com
/(bb)|^b{2}/
--Shakespeare | [reply] |
Re: quick question about hash element access
by antirice (Priest) on Jun 28, 2003 at 04:58 UTC
|
How come everyone else gets to have the fun? Let's see if you'll let me in with this little hack that lets you play with all sorts of mixtures of structures...hashes, and arrays, and anonymous subs...OH MY! And in case something goes haywire and the key you specify doesn't exist [:'(] have no fear, it just returns undef and allows you to go along on your merry way! Follow the yellow br...err...I mean...umm...here's the code:
#!/usr/bin/perl
require 5.6.1;
use strict;
my $hash;
$hash = {
a => {
b=> [
\\\$hash,
{
c => sub {
$_ = shift;
return [ join "","j",$_,"h" ];
}
}
]
}
};
sub retrievekey {
my $endvalue = shift;
my $keys = shift;
my @keys = split /\./, $keys;
foreach my $x (@keys) {
return undef unless defined $endvalue;
$endvalue = $$endvalue while (ref($endvalue) eq "REF");
eval {
if (ref($endvalue) eq "HASH") {
$endvalue = $endvalue->{$x};
} elsif (ref($endvalue) eq "ARRAY") {
$endvalue = $endvalue->[$x];
} elsif (ref($endvalue) eq "CODE") {
$endvalue = $endvalue->($x);
} else { return undef; }
};
return undef if ($@);
}
$endvalue;
}
print retrievekey($hash, 'a.b.0.a.b.1.c.ap.0'),$/;
__DATA__
output:
japh
Hey, it derefs any references to references and follows paths for hashes, arrays, and subs. This is just a silly bit of fun. Of course, don't use this or whoever maintains the code will hunt you down and shoot you... and nobody wants that... at least I don't :-P
antirice The first rule of Perl club is - use Perl The ith rule of Perl club is - follow rule i - 1 for i > 1 | [reply] [d/l] |
Re: quick question about hash element access
by shemp (Deacon) on Jun 27, 2003 at 21:37 UTC
|
Just wondering, why are you using an identifier like 'a.b.c.b'. It occurs to me that if i were doing this, id probably have the identifier be an array (ref), i.e.
my @identifier = qw(a b c d);
...
| [reply] [d/l] |
Re: quick question about hash element access
by DrHyde (Prior) on Jun 28, 2003 at 12:20 UTC
|
Very quick and simple solution:
$hash{a}{b}{c}{d}{e} = 'E';
print getthingy(\%hash, 'a.b.c.d.e');
sub getthingy {
my($hash, $key) = @_;
$hash = $hash->{$_} foreach(split/\./, $key);
$hash;
}
| [reply] [d/l] |
Re: quick question about hash element access
by glwtta (Hermit) on Jun 28, 2003 at 18:00 UTC
|
Thanks for all the responses, as I suspected people have managed to come up with quite a few things I haven't thought of.In my case, I eneded up doing something much less cool, but more functional - since I only need the hash for read-only access, I just "flatten" the hash-of-hashes-of-hashes... into a simple hash with strings I wanted ('a.b.c.d') as the keys when I load it, and go from there - not as fun, but quicker.
Thanks, again. | [reply] |