datenpirat
datenpirat

Reputation: 65

Adressing a hash of hashes with an array

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?

Upvotes: 2

Views: 122

Answers (6)

Mike
Mike

Reputation: 21

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

Upvotes: 0

cdtits
cdtits

Reputation: 1128

$pname = '/etc/conf/test.file';
@names = split '/', $pname;
$fh = \%fs;
for (@names) {
    $fh = $fh->{"$_"} if $_;
}
print $fh->{'content'};

Upvotes: 0

tuxuday
tuxuday

Reputation: 3037

Same logic as what others given, but uses foreach

@keys = qw(etc conf test.file content);
$r = \%fs ;
$r = $r->{$_} foreach (@keys);
print $r;

Upvotes: 1

Borodin
Borodin

Reputation: 126722

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;
}

Upvotes: -1

perreal
perreal

Reputation: 97918

my @a = ("etc","conf","test.file");

my $h = \%fs;
while (my $v = shift @a) {
  $h = $h->{$v};
}
print $h->{type};

Upvotes: 1

choroba
choroba

Reputation: 241738

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.

Upvotes: 5

Related Questions