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

Hello, I've been trying to figure out/understand how to create hash of hashes from an input file. I want to be able to create references to each element using variables. Not exactly sure how to actually create the hashes from an input file. Here is a small sample of what my data file looks like my actual file contains all my users data:
FullName : User1 Home Address : 111 address lane Phone : 555-555-5555 FullName : User2 Home Address : 222 address lane 2 Phone : 777-777-7777
Here is the code that I've been playing with so far:
#!/usr/bin/perl use strict; my $myfile = $ARGV[0]; open FILE, "<", $myfile || die "No open $!\n"; my %hash; while (<FILE>) { chomp; my ($key, $val) = split /:/; $hash{$key} .= exists $hash{$key} ? ",$val" : $val; } foreach my $key (keys %hash) { print "$key = $hash{$key}\n"; }
Trying to create groupings name, address, phone using the LABELS on the left side ("FullName", "Home Address", "Phone"). I am trying to use the key $name and print all data for that $name variable. output example:
User: User1 111 address lane 555-555-5555 User: User2 222 address lane 2 777-777-7777
This way I can actually map all FullNames to actual names all Phone to Phone number etc.. I tried creating a reference to an anonymous hash, but the problem is not sure how to only store all names all phone# etc in each hash.
$hash{$x}= { Name => $name, Addr => $address, Phone => $phone
Not sure how to go about this any help would be very much appreciated as I am not getting any where here. I can accomplish this using awk/cut but I started to play around with it in Perl and never really used hashes so I thought this would be a great time to learn/try to understand it. Thanks for all the help in advance. FYI:

Replies are listed 'Best First'.
Re: creating hash of hashes from input file
by BrowserUk (Patriarch) on Jul 17, 2010 at 14:32 UTC

    #! perl -slw use strict; use Data::Dump qw[ pp ]; $Data::Dump::WIDTH = 50; $/ = ''; ## para mode; see Perlvar my %hash; while( <DATA> ) { my %subhash; for my $line ( split "\n" ) { my( $key, $value ) = split /\s+:\s+/, $line; $subhash{ $key } = $value; } $hash{ $subhash{ FullName } } = \%subhash; } pp \%hash; __DATA__ FullName : User1 Home Address : 111 address lane Phone : 555-555-5555 FullName : User2 Home Address : 222 address lane 2 Phone : 777-777-7777

    Produces:

    c:\test>junk7 { User1 => { FullName => "User1", "Home Address" => "111 address lane", Phone => "555-555-5555", }, User2 => { FullName => "User2", "Home Address" => "222 address lane 2", Phone => "777-777-7777", }, }

      I'm glade to see my snippet is similar with BrowserUk's ;)

      Let me ask a question slightly OT:

      Why many monks don't like use parenthesis with functions such as split, map, print. is it because concise, readable or other reasons?





      I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction

        Why many monks don't like use parenthesis with functions such as split, map, print. is it because concise, readable or other reasons?

        For the same reason most people don't type:

        my sum = (((($p+$q)+$r)+$s)+t);

        when

        my $sum = $p + $q + $r + $s + $t;

        is cleaner and clearer.

        Perhaps more realistically, my $z = sqrt( $x**2 + $y**2 );

        in preference to my $z = sqrt (($x**2)+($y**2));.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
        is it because concise, readable or other reasons?
        Yes. Paraphrasing the Perl Best Practices book:
        • Reduces clutter
        • Enhances readability
        • Lack of parentheses visually distinguishes builtins from subroutine calls

        The book is full of well-articluated reasons for its many coding style recommendations.

      Thanks for the reply. I am not quite sure I follow everything you did in your code. So how do I access the elements without using pp \%hash? For example if I want output like shown here: With out the use of pp \%hash how to I print each element. Tried doing what I was doing before with a foreach but I just get the HASHREF# not the actual data.
      User User1 111 address lane 555-555-5555 User User2 222 address lane 2 777-777-7777

        #! perl -slw use strict; use Data::Dump qw[ pp ]; $Data::Dump::WIDTH = 50; $/ = ''; my %hash; while( <DATA> ) { my %subhash; for my $line ( split "\n" ) { my( $key, $value ) = split /\s+:\s+/, $line; $subhash{ $key } = $value; } $hash{ $subhash{ FullName } } = \%subhash; } ##pp \%hash; for my $primaryKey ( keys %hash ) { print "$primaryKey:"; for my $subKey ( keys %{ $hash{ $primaryKey } } ) { print "\t$subKey => $hash{ $primaryKey }{ $subKey }"; } } __DATA__ FullName : User1 Home Address : 111 address lane Phone : 555-555-5555 FullName : User2 Home Address : 222 address lane 2 Phone : 777-777-7777

        Gives:

        c:\test>junk7 User1: Phone => 555-555-5555 Home Address => 111 address lane FullName => User1 User2: Phone => 777-777-7777 Home Address => 222 address lane 2 FullName => User2
Re: creating hash of hashes from input file
by biohisham (Priest) on Jul 17, 2010 at 17:28 UTC
    Here's just a Tim Toady that simulates a file parser that reads the lines one by one into variables corresponding to each record's parameters (Full name, address, phone number) and then splits these around a semi-colon. It defines a counter $counter that is incremented at the end of each record and appended to the hash key.Note that you don't need a blank line to separate the records since this code assigns each line it reads to a parameters in each record in tandem ..

    Check Tutorials->References and perldsc to delve deeper into the world of advanced data structures in Perl.

    #!/usr/local/bin/perl use strict; use Data::Dumper; my %hash; my $counter = 0; until(eof(DATA)){ chomp (my $fullName = <DATA>); my ($name, $val1) = split /\s+:\s+/,$fullName; chomp (my $homeAddr = <DATA>); my ($addr, $val2) = split /\s+:\s+/,$homeAddr; chomp (my $phoneNo = <DATA>); my ($phone, $val3) = split /\s+:\s+/,$phoneNo; $hash{"user".++$counter} = {$name=>$val1, $addr=>$val2, $phone +=>$val3}; } print Dumper(\%hash); __DATA__ FullName : User1 Home Address : 111 address lane Phone : 555-555-5555 FullName : User2 Home Address : 222 address lane 2 Phone : 777-777-7777
    $VAR1 = { 'user1' => { 'Phone' => '555-555-5555', 'Home Address' => '111 address lane', 'FullName' => 'User1' }, 'user2' => { 'Phone' => '777-777-7777', 'Home Address' => '222 address lane 2', 'FullName' => 'User2' } };

    Update: Added more clarifications...


    Excellence is an Endeavor of Persistence. A Year-Old Monk :D .
Re: creating hash of hashes from input file
by toolic (Bishop) on Jul 17, 2010 at 23:55 UTC
    open FILE, "<", $myfile || die "No open $!\n";
    Unrelated to your problem, but this line has a bug. Because of operator precedence, it will not die if the file can not be opened, as you desire. It will only die if $myfile is 0 or undefined. Here is one way to fix the bug:
    open FILE, "<", $myfile or die "No open $!\n";

    Furthermore, it is now common practice to use lexical filehandles:

    open my $fh, '<', $myfile or die "No open $!\n"; while (<$fh>) {

    This can be simplified using autodie (part of Core starting with Perl 5.10.1):

    use autodie; open my $fh, '<', $myfile; while (<$fh>) {
Re: creating hash of hashes from input file
by xiaoyafeng (Deacon) on Jul 17, 2010 at 14:27 UTC

    is below code OK?

    my $myfile = $ARGV[0]; open FILE, "<", $myfile || die "No open $!\n"; local $/; my $a = <FILE>; my @t = split(/^\n/m, $a);

    Besides:

    $hash{$key} .= exists $hash{$key} ? ",$val" : $val;

    above code will write user name one more times if it has. I guess it might not be what you want.





    I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction