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

I need to nest an anonymous hash and an anonymous array inside a hash, and beg help from the wisdom of the monastery to supplement my weak understanding. The data to be managed is like this (almost the same as the windows registry, fwiw) plus the line of (wrong) code to store it:
item 1 # as read from file $key = "k1"; $valname = "vname1"; $value = "v1"; $type = "t1"; $flag = "f1"; $hash{ $key } = ( { $valname => [ $value, $type, $flag ] }); item 2 $key = "k1"; $valname = "vname2"; $value = "v2"; $type = "t2"; $flag = "f2"; $hash{ $key } = ( { $valname => [ $value, $type, $flag ] }); item 3 $key = "k3"; $valname = "vname3"; $value = "v3"; $type = "t3"; $flag = "f3"; $hash{ $key } = ( { $valname => [ $value, $type, $flag ] }); item N (indeterminate until runtime)
when stored it should be something like this:
k1 => vname1 => (v1, t1, f1) vname2 => ( v2, t2, f2 ) k3 => vname3 => ( v3, t3, f3 )
Note that both items 1 and 2 have the same value for $key. My attempt at storing the data is wrong, so item2 overwrites item1 and k1's inner (anonymous) hash has only item2's data.
foreach $i (keys %hash) { print "i=$i\n"; foreach $j (keys %{$hash{$i}} ) { @content = $hash{$i}->{$j}; #print "\tvalname=$j content=$$hash{$i}->{$j}[1] \n"; print "\tj=$j val=$hash{$i}->{$j}[0] type=$hash{$i}->{$j}[1] +flag=$hash{$i}->{$j}[2] \n"; print "$content[0] $content[1] $content[2]\n"; } }
The loop to access the data isn't good either -- somewhere the perl's too slippery for me -- so any hints or even better solutions would be wonderful.
Thanks!

Replies are listed 'Best First'.
Re: inner anonymous hash
by friedo (Prior) on Feb 13, 2005 at 07:01 UTC
    Since $key can be duplicate, it might make sense to have it point to an array that you can push records onto. For example:

    push @{$hash{$key}}, { $valname => [ $value, $type, $flag ] };

    You could then cycle over the structure like this:

    foreach my $i(keys %hash) { print "i=$i\n"; foreach my $j(@{$hash{$i}}) { my ($valname) = keys %{ $j }; print "valname = $valname\n"; print "content = ", $j->{$valname}[0], "\n"; # etc. } }
Re: inner anonymous hash
by injunjoel (Priest) on Feb 13, 2005 at 10:11 UTC
    Greetings,
    friedo++ on how to solve this particular problem so I will point you at some helpful docs.
    perlref (about anonymous and referenced structures) & Dumpvalue or Data::Dumper (both of which will help make sure you are creating what you think you are creating).
    quick usage example:
    #!/usr/bin/perl -w use strict; use Dumpvalue; print "dumping data structure\n"; dump_ref(\$reference_to_your_nested_data_structure); exit; #the sub. sub dump_ref { my $d = new Dumpvalue; my $ref = shift; $d->dumpValues($ref); }
    "I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo
Re: inner anonymous hash
by bobf (Monsignor) on Feb 13, 2005 at 19:17 UTC

    You're close, but based on your desired data structure I think what you really want is a hash of a hash of arrays:

    k1 => vname1 => [ v1, t1, f1 ] # note: I changed the () to [] vname2 => [ v2, t2, f2 ] k3 => vname3 => [ v3, t3, f3 ]

    To create this type of structure, simply assign values like this:

    # Assign an array (ref) to the HoH value $hash{$key}{$valname} $hash{$key}{$valname} = [ $value, $type, $flag ]; # option 1 @{ $hash{$key}{$valname} } = ( $value, $type, $flag ); # option 2
    Given a $key and $valname, retrieve them like this:
    my $type = ${ $hash{$key}{$valname} }[1]; # individual value my @array = @{ $hash{$key}{$valname} }; # all values
    I prefer to use the bracket method of dereferencing (rather than the arrow method) because it helps me differentiate between object method calls and dereferences, but it adds keystrokes.

    To print all of the data:

    foreach my $key ( keys %hash ) { print "$key:\n"; foreach my $valname ( keys %{ $hash{$key} } ) { print " $valname: "; print join( "\t", @{ $hash{$key}{$valname} } ), "\n"; } }
    Data::Dumper is very useful here, too:
    use Data::Dumper; print Dumper( \%hash );

    See the docs (perlref, perldsc) for more info.

    HTH

Re: inner anonymous hash
by brian_d_foy (Abbot) on Feb 13, 2005 at 18:53 UTC

    The perldsc page (dsc -> Data Structures Cookbook) show you how to do that.

    Good luck :)

    --
    brian d foy <bdfoy@cpan.org>
Re: inner anonymous hash
by anadem (Scribe) on Feb 14, 2005 at 16:46 UTC
    Many thanks for all the advice, and the pointers to docs. The Data Structures Cookbook is going to be very useful, and Data::Dumper is a great tool (perl debugger's 'x <data>' is a decent alternative). Special thanks to bobf for intuiting the structure I was groping for, and giving the explicit detail. Much appreciated, thank you all!