2

This is my problem:

I have a file-system like data-structure:

%fs = (
    "home" => {
        "test.file"  => { 
            type => "file",
            owner => 1000, 
            content => "Hello World!",
        },
    },
    "etc"  => { 
        "passwd"  => { 
            type => "file",
            owner => 0, 
            content => "testuser:testusershash",
            },
        "conf"  => { 
            "test.file"  => { 
                type => "file",
                owner => 1000, 
                content => "Hello World!",
            },
        },
    },
);

Now, to get the content of /etc/conf/test.file I need $fs{"etc"}{"conf"}{"test.file"}{"content"}, but my input is an array and looks like this: ("etc","conf","test.file").

So, because the length of the input is varied, I don't know how to access the values of the hash. Any ideas?

Axeman
  • 29,660
  • 2
  • 47
  • 102
datenpirat
  • 65
  • 4
  • 2
    Related: http://stackoverflow.com/questions/8671233/programatic-access-of-a-hash-element http://stackoverflow.com/questions/9789420/how-would-you-create-and-traverse-a-hash-of-hashes-of-depth-n-whereby-the-val http://stackoverflow.com/questions/10965006/convert-string-a-b-c-to-hash-a-b-c-in-perl – daxim Jul 31 '12 at 12:29

6 Answers6

5

You can use a loop. In each step, you proceed one level deeper into the structure.

my @path = qw/etc conf test.file/;
my %result = %fs;
while (@path) {
    %result = %{ $result{shift @path} };
}
print $result{content};

You can also use Data::Diver.

choroba
  • 231,213
  • 25
  • 204
  • 289
1
my @a = ("etc","conf","test.file");

my $h = \%fs;
while (my $v = shift @a) {
  $h = $h->{$v};
}
print $h->{type};
perreal
  • 94,503
  • 21
  • 155
  • 181
1

Same logic as what others given, but uses foreach

@keys = qw(etc conf test.file content);
$r = \%fs ;
$r = $r->{$_} foreach (@keys);
print $r;
tuxuday
  • 2,977
  • 17
  • 18
0
$pname = '/etc/conf/test.file';
@names = split '/', $pname;
$fh = \%fs;
for (@names) {
    $fh = $fh->{"$_"} if $_;
}
print $fh->{'content'};
cdtits
  • 1,118
  • 6
  • 7
0

Path::Class accepts an array. It also gives you an object with helper methods and handles cross platform slash issues.

https://metacpan.org/module/Path::Class

Mike
  • 21
  • 2
-1

You can just build the hash element expression and call eval. This is tidier if it is wrapped in a subroutine

my @path = qw/ etc conf test.file /;

print hash_at(\%fs, \@path)->{content}, "\n";

sub hash_at {
  my ($hash, $path) = @_;
  $path = sprintf q($hash->{'%s'}), join q('}{'), @$path;
  return eval $path;
}
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • Can the down voters please explain? There is no problem with `eval` as long as the source of the string is reliable. Here we have built it ourselves so it is trustworthy. – Borodin Jul 31 '12 at 12:37