Reputation: 35
I've created a hash of hashes in perl, where this is an example of what the hash ends up looking like:
my %grades;
$grades{"Foo Bar"}{Mathematics} = 97;
$grades{"Foo Bar"}{Literature} = 67;
$grades{"Peti Bar"}{Literature} = 88;
$grades{"Peti Bar"}{Mathematics} = 82;
$grades{"Peti Bar"}{Art} = 99;
and to print the entire hash, I'm using:
foreach my $name (sort keys %grades) {
foreach my $subject (keys %{ $grades{$name} }) {
print "$name, $subject: $grades{$name}{$subject}\n";
}
}
I need to print just the inner hash referring to "Peti Bar" and find the highest value, so theoretically, I should just parse through Peti Bar, Literature; Peti Bar, Mathematics; and Peti Bar, Art and end up returning Art, since it has the highest value. Is there a way to do this or do I need to parse through the entire 2d hash?
Upvotes: 3
Views: 815
Reputation: 63932
As a Perl beginner I would use the List::Util core module:
use 5.014;
use List::Util 'reduce';
my $k='Peti Bar';
say reduce { $grades{$k}{$a} > $grades{$k}{$b} ? $a : $b } keys %{$grades{$k}};
Upvotes: 0
Reputation: 126732
This is trivial using the List::UtilsBy
module
The code is made clearer by extracting a reference to the inner hash that we're interested in. The max_by
is called to return the keys of that hash that has the maximum value
use strict;
use warnings 'all';
use feature 'say';
use List::UtilsBy 'max_by';
my %grades = (
'Foo Bar' => { Literature => 67, Mathematics => 97 },
'Peti Bar' => { Literature => 88, Mathematics => 82, Art => 99 },
);
my $pb_grades = $grades{'Peti Bar'};
say max_by { $pb_grades->{$_} } keys %$pb_grades;
Art
Upvotes: 2
Reputation: 54333
You don't need to parse through the first level if you know the key that you're interested. Just leave out the first loop and access it directly. To get the highest value, you have to look at each subject once.
Keep track of the highest value and the key that goes with it, and then print.
my $max_value = 0;
my $max_key;
foreach my $subject (keys %{ $grades{'Peti Bar'} }) {
if ($grades{'Peti Bar'}{$subject} > $max_value){
$max_value = $grades{'Peti Bar'}{$subject};
$max_key = $subject;
}
}
print $max_key;
This will output
Art
An alternative implementation with sort
would look like this:
print +(
sort { $grades{'Peti Bar'}{$b} <=> $grades{'Peti Bar'}{$a} }
keys %{ $grades{'Peti Bar'} }
)[0];
The +
in +( ... )
tells Perl that the parenthesis ()
are not meant for the function call to print
, but to construct a list. The sort
sorts on the keys, descending, because it has $b
first. It returns a list, and we take the first value (index 0
).
Note that this is more expensive than the first implementation, and not necessarily more concise. Unless you're building a one-liner or your ; is broken I wouldn't recommend the second solution.
Upvotes: 2