Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

building a hash of hashes with constant keys

by mooseboy (Pilgrim)
on Nov 19, 2002 at 08:53 UTC ( #214056=perlquestion: print w/replies, xml ) Need Help??

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

Fellow monks, I seek enlightenment on how to generate a hash of hashes with constant keys. I have a list of countries in an array like so: @countries = (Algeria, Argentina...) which will form the keys of the inner hashes, and a file containing data in the following format:
Q4_2001: 741 1203 3406 1861 1242 1911 601 etc Q3_2001: 773 1255 3552 1941 1296 1993 627 etc Q2_2001: 805 1307 3698 2021 1350 2075 653 etc
I would like to build this into a hash of hashes thus:
%HoH = ( Q4_2001 => { Algeria => 741, Argentina => 1203, etc }, Q3_2001 => { Algeria => 773, Argentina => 1255, etc }, );
However, I am fairly new to Perl, and my attempts to write a sub to generate this HoH have not met with success, even after much perusal of perldsc, perllol, etc. Here's what I have so far:
sub build_HoH { while (<DATA>) { next unless s/^(.*?):\s*//; my $period = $1; my @data = split; foreach my $country(@countries) { $HoH{$period}{$country} = $data; } }
I would be grateful for any brotherly advice as to where I am going wrong. Humble thanks in advance.

Replies are listed 'Best First'.
Re: building a hash of hashes with constant keys
by Zaxo (Archbishop) on Nov 19, 2002 at 09:43 UTC

    You can do that pretty conveniently. First set up the unchanging country list as an array (not with qw() since some countries have more than one word in the name):

    my @countries = ( 'Algeria', 'Argentina', 'Zaire', );

    Now prepare to read the data file,
    my %HoH; open INFILE, '< /path/to/datafile.txt' or die $!;
    The file contains our top level keys with colon as a delimiter, and the second level values in order corresponding to the country array (In my opinion that is a weakness of the design, a new country forming will wreck things for you). We will split each line first on colon to get the top key, then we'll do magic split on space to provide the values. We can get an efficient and compact assignment using a hash slice.
    while (<INFILE>) { ( my $quarter, $_) = split ':'; @{$HoH{$quarter}}{@countries} = split " "; # repaired typo, ++petr +al }
    Both lines in that read loop are a little unusual. my usually is seen outside the parens, but only $quarter is to be lexical. The rat's nest of curlies applies @ to a hash reference, setting up the slice. close INFILE or die $!; All done!

    After Compline,

      Thanks Zaxo, works a treat! Also thanks to Robartes for the Data::Dumper suggestion, it looks like an interesting alternative I ought to explore. I hope to post the complete code in a day or so, since this is very first Perl program I have ever written (hey, we all have to start somewhere) and I am sure the collective wisdom of the Monks will vastly improve my Initiate-level effort. Cheers, mooseboy
Re: building a hash of hashes with constant keys
by robartes (Priest) on Nov 19, 2002 at 09:05 UTC
    Just one way of doing it:
    use strict; use Data::Dumper; my @countries=('Argentina', 'Algeria', 'Belgium'); my %HoH; while (<DATA>) { my ($period, @values)=split; my %temphash; $temphash{$countries[$_]}=$values[$_] for (0..scalar @countries -1); $HoH{$period}=\%temphash; } print Dumper(\%HoH); __DATA__ Q4_2001 1 2 3 Q1_2002 4 5 6


    Update: Replaced qw// style quoting. See Zaxo's reply below as to why. Good catch, Zaxo!

Re: building a hash of hashes with constant keys
by Three (Pilgrim) on Nov 19, 2002 at 14:24 UTC

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://214056]
Approved by robartes
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (7)
As of 2021-06-25 09:12 GMT
Find Nodes?
    Voting Booth?
    What does the "s" stand for in "perls"? (Whence perls)

    Results (134 votes). Check out past polls.