http://www.perlmonks.org?node_id=848928

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

I've a hash of hash like,
Year = { 2007 = { 01 => 07, 02 => 34, 10 => 24, 09 => 14, } 2008 = { 01 => 11, 02 => 64, 03 => 20, 09 => 13, } }
where I want to get the maximum and minimum of all years. So, result will be 02 in 2008 is maximum and 01 in 2007 is the minimum.
I've tried to get this in different ways. But, the code is larger like writing in conventional languages. Someone help me write this smarter.

Replies are listed 'Best First'.
Re: min and max in a hash of hash
by ikegami (Patriarch) on Jul 12, 2010 at 06:12 UTC
    my ($max_y) = my ($min_y) = keys(%Year); my ($max_m) = my ($min_m) = keys(%{ $Year{$max_y} }); my $max = $Year{$max_y}{$max_m}; my $min = $Year{$min_y}{$min_m}; for my $y (keys(%Year)) { my $Month = $Year{$y}; for my $m (keys(%$Month)) { my $v = $Month->{$m}; if ($v < $min) { $min = $v; $min_y = $y; $min_m = $m; } elsif ($v > $max) { $max = $v; $max_y = $y; $max_m = $m; } } }

    Update: I was initialising $max_y twice instead of initialising $min_m. Fixed.

Re: min and max in a hash of hash
by Marshall (Canon) on Jul 12, 2010 at 09:32 UTC
    One way to do this is to transform the hash structure into an AoA so that it is easier to sort, then take first (index 0) and last (index -1) to get min and max. This type of approach allows easy reporting "ties" for min or max if that is important to you, eg maybe the minimum occurs on three different dates. Perl is very good at sorting and this runs faster than one might imagine.

    #!/usr/bin/perl -w use strict; use Data::Dumper; my %years =( 2007 => { '01' => 07, '02' => 34, '10' => 24, '09' => 14, }, 2008 => { '01' => 11, '02' => 64, '03' => 20, '09' => 13, }, ); my @year_data; foreach my $year (keys (%years)) { foreach my $month (keys %{$years{$year}}) { push (@year_data, [$year, $month, $years{$year}{$month}]); } } @year_data = sort { my ($yearA, $monthA, $dataA) = @$a; my ($yearB, $monthB, $dataB) = @$b; $dataA <=> $dataB or $monthA <=> $monthB or $yearA <=> $yearB }@year_data; print "minimum is: @{$year_data[0]}\n"; print "maximum is: @{$year_data[-1]}\n"; #minimum is: 2007 01 7 #maximum is: 2008 02 64
Re: min and max in a hash of hash
by AnomalousMonk (Archbishop) on Jul 12, 2010 at 20:16 UTC
    ... 01 => 07, ...

    Just a note of caution: The number representation 07 (octal radix) has the same value for all radices in which '7' is a valid digit, e.g., 7 and 0x7; something like 077 has not. Don't tempt Fate.

    >perl -wMstrict -le "print 077" 63
Re: min and max in a hash of hash
by ikegami (Patriarch) on Jul 12, 2010 at 06:04 UTC
    use List::Util qw( min max ); my $max_y = max keys %Year; my $max_m = max keys %{ $Year{$max_y} }; my $min_y = min keys %Year; my $min_m = min keys %{ $Year{$min_y} };
      Hi ikegami, could you enlighten me what is wrong on using List::Util? the following code works fine, after I corrected the hash provided:
      foreach my $y (sort keys %Year) { my $min_y = List::Util::min values %{ $Year{$y} }; print "min of [$y] is [$min_y]\n"; }
      Edit: this works for sure only if you doesn't care about the month of the min/max value.

        That will give you the minimum value for each year. The OP wanted just the overall minimum. If he doesn't need the y/m of the minimum, then he could use

        use List::Util qw( min max ); my $min = min map { min values %{$Year{$_}} } keys %Year; my $max = max map { max values %{$Year{$_}} } keys %Year;

        Update: Fixed s/values/keys/ as per reply.