2

Let's assume that I have a complex hash reference $hash_ref, and I would like to access data in it by doing something like this:

my $string1 = "{books}";
my $string2 = "{31335}->{book_name}";
print Dumper($hash_ref->$string1->$string2);

Of course, this doesn't work, but I hope it explains what I'd like to do.

Obviously, there are many ways I can make this work, but I am (out of curiosity) really interested to figure out if there is some Perl magic that could make this work without splitting strings, etc.

I know that I could create 3 strings ("books", "31335", "book_name") and have this done in a second, and there are certainly other ways, but I never understood if one could actually access hash data by using strings that represent hash structure, like in the above example.

Thanks :)

2
  • well whats happens if you try it? a hash key can in theory be any string .... Commented Aug 25, 2010 at 11:31
  • Get an error: Can't call method "{books}" on unblessed reference Commented Aug 25, 2010 at 11:38

5 Answers 5

6

It can be done using eval. However, just because some can be done doesn't mean it should.

use strict;
use warnings;

my $hr = { books => { 31335 => { book_name => 'FOO' } } };

my $k1 = "{books}";
my $k2 = "{31335}->{book_name}";

my $f = eval "\$hr->$k1->$k2";  # Don't do this. It's a terrible idea.
print $f, "\n";                 # FOO

You should bite the bullet and extract the keys from the strings:

my @ks = "$k1$k2" =~ /\{ \s* (.+?) \s* \}/gx;
$f = $hr;
$f = $f->{$_} for @ks;
print $f, "\n";                 # FOO
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for your answer. I appreciate other answers too, but this one really answered everything I wanted to know. I didn't think it was possible, but I forgot about eval completely. Now I know that it can be done (not that I'll be doing it, though :). Thanks once again.
1

I am in no way saying that this is a good idea, but here is how to do it (without eval):

use strict;
use warnings;

my $hash_ref = {books => {31335 => {book_name => 'perl'}}};

my $key = sub {
    my $hash = shift;
    my @keys = grep {s!^\{|\}$!!g; $_} split /->/ => "@_";
    $hash = $$hash{$_} for @keys;
    $hash
};

my $string1 = "{books}";
my $string2 = "{31335}->{book_name}";

print $hash_ref->$key($string1)->$key($string2);  # prints 'perl'

or, to keep the calling code a little cleaner, you can code up a boxing class to handle arbitrary strings as method calls:

sub key_methods {bless \$_[0] => 'KeyMethods'}

{package KeyMethods;
    use overload nomethod => sub {${$_[0]}},  # automatic unboxing
                    '%{}' => sub {${$_[0]}};
    sub AUTOLOAD {
        my $ret = ${$_[0]}->$key(our $AUTOLOAD =~ /([^:]+)$/);

        ref $ret eq 'HASH'
            ? bless \$ret
            : $ret;
    }
}

print key_methods($hash_ref)->$string1->$string2;  # prints 'perl'

2 Comments

Really appreciate this answer. I just need to decrypt it first, but I'm getting there :) Thanks once again.
@sentinel => is there any particular part you would like explained?
0

If you are more interested in having a variable store a 'path' to use in accessing a data structure, rather than being able to use a string that can be user submitted or dynamically generated, then you can use an lvalue anonymous sub.

my $deref1 = sub :lvalue { $_[0]->{books} };
my $deref2 = sub :lvalue { shift->{31335}{book_name} }; # if you don't like $_[0]

my $hash_ref = { };

# :lvalue on the sub allow it to be assigned to
$hash_ref->$deref1 = { 31335 => { book_name => 'FOO' } };

print $hash_ref->$deref1->$deref2, "\n";

Comments

-1
my $string1 = "{books}";
my $string2 = "{31335}->{book_name}";
my $hash_ref = { key1 => { books =>  { 31335 =>  { book_name => "gold" }}}};
my $hash_ref_name = "\$hash_ref";
my $str = join("->", $hash_ref_name, "{key1}", $string1, $string2);
print eval $str, "\n"

Comments

-3

AFAIK there is no already available thing which can do this,you have to write a wrap up code to make it possible.I think hash implementation and functions are really simple and cool,you can make it work in very less code.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.