Kerplunk has asked for the wisdom of the Perl Monks concerning the following question:

Forgive me a rather elementary question about hashes. I have a hash of arguments that I would like to pass to a subroutine. Some of the arguments are themselves hashes, e.g.:

my %userHash; $userHash{$thisUID}{'name'} = 'Some Charming User'; $userHash{$thisUID}{'alias'} = 'some.charming.user'; my %args = (users => %userHash, domains => %domainHash, path => $path) +;

What I'm confused about is how to treat the hash arguments within the subroutine. This, for instance, doesn't give me the result I was expecting:

doSomethingUseful(\%args); sub doSomethingUseful { my $params = shift; my %args = %$params; my %users = $args{users}; my %domains = $args{domains}; my $path = $args{path}; if (keys %users > 1) { print("I was hoping you'd say that."); } }

Replies are listed 'Best First'.
Re: Hash as Hash Value
by kennethk (Abbot) on Apr 20, 2010 at 19:39 UTC
    Part of your problem is that your constructor doesn't do what you think. When you write

    my %args = (users => %userHash, domains => %domainHash, path => $path)

    Perl sees %userHash and %domainHash in list context, and so it expands them into lists. If your code read

    my %userHash = (user1 => 'uid'); my %domainHash = (domain1 => 'uri');

    your %args assignment would actually say

    my %args = (users => 'user1', uid => 'domains', domain1 => 'uri', path => $path);

    You need, rather, to use hash refs to maintain the hash structure when you define %args:

    my %args = (users => \%userHash, domains => \%domainHash, path => $path);

    See perlreftut and/or perllol for information on how to represent nested hash structures (or perldsc or perlref or ...).

Re: Hash as Hash Value
by toolic (Bishop) on Apr 20, 2010 at 19:33 UTC
    I think you want to use hash references inside your hash. Change:
    my %args = (users => %userHash, domains => %domainHash, path => $path) +;

    to:

    my %args = (users => \%userHash, domains => \%domainHash, path => $pat +h);

    Data::Dumper is very handy in debugging Perl data structure problems:

    print Dumper(\%args);

    Update: It looks like you were unlucky in the sense that you happened to have an even number of hashes in your %args hash. If you had had an odd number of hashes, you should have gotten a warning message complaining about odd number of elements in hash assignment (assuming you are using warnings):

    my %args = (users => %userHash, domains => %domainHash, third => %hash +, path => $path);
Re: Hash as Hash Value
by lostjimmy (Chaplain) on Apr 20, 2010 at 19:37 UTC
    Your main problem is that the values of hashes can only be scalars, and not entire hashes. Lucky for you, a hash references (hashref) is also a scalar. You want to create your %args hash like this: my %args = (users => \%userHash, domains => \%domainHash, path => $path);

    In your subroutine, you have to dereference the hashrefs in order to use them.

    sub doSomethingUseful { my $args = shift; my $users = $args->{users}; my $domains = $args->{domains}; my $path = $args->{path}; if (keys %$users > 1) { print("I was hoping you'd say that."); } }
Re: Hash as Hash Value
by Kerplunk (Acolyte) on Apr 21, 2010 at 13:21 UTC

    Thank you for the responses. I'm not sure why I'm having such a struggle with hash referencing and de-referencing, but this helped.

    Given my previous example, suppose I have something like this within my subroutine:

    my $hashOfUsers = $args->{users}

    Why does this:

    $hashOfUsers{$someKey}{UID}

    not return the value of UID for the user identified by key $someKey?

      When you write $hashOfUsers{$someKey}{UID}, perl automatically rewrites it internally as $hashOfUsers{$someKey}->{UID}, using The Arrow Operator to dereference. This is then interpreted as:

      1. Get the value from the hash %hashOfUsers corresponding to $someKey
      2. deference it as a hash (->{}) and
      3. get the key associated with UID."

      If $hashOfUsers is a hashref and you want to access that, you want the syntax $hashOfUsers->{$someKey}{UID}. Perl can't automatically know to deference $hashOfUsers because then $hashOfUsers{$someKey} would be ambiguous in the case where you had both a hash and a scalar named hashOfUsers. It can automatically deference the second bracket since the syntax is unambiguous.

      Because $hashOfUsers is a scalar, not a hash. It is a hash reference, though, so you can use
      $hashOfUsers->{$someKey}{UID}
        Drat!