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,
    Zaxo

      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

    CU
    Robartes-

    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