Perl hash hash and count
I am trying to parse a perl data structure that I dumped with Data :: Dumper
$VAR1 = 'GAHD';
$VAR2 = [
{ 'COUNTRY' => 'US',
'NAME' => 'K. Long',
'DATE_OF_BIRTH' => '7/27/1957',
'POSITION' => 'SENIOR OFFICER',
'AGE' => 57,
'GRADE' => 'P5'
},
{ 'COUNTRY' => 'US',
'NAME' => 'J. Buber',
'DATE_OF_BIRTH' => '12/11/1957',
'POSITION' => 'CHIEF',
'GRADE' => 'D1'
},
{ 'COUNTRY' => 'US',
'NAME' => 'M. Amsi',
'DATE_OF_BIRTH' => '1/1/1957',
'POSITION' => 'SENIOR ANIMAL HEALTH OFFICER',
'AGE' => 57,
'GRADE' => 'P5'
},
{ 'COUNTRY' => 'US',
'NAME' => 'E. Xenu',
'DATE_OF_BIRTH' => '8/31/1964',
'POSITION' => 'SENIOR OFFICER',
'AGE' => 50,
'GRADE' => 'P5'
},
];
$VAR3 = 'GAGD';
$VAR4 = [
{ 'COUNTRY' => 'US',
'NAME' => 'P. Cheru',
'DATE_OF_BIRTH' => '6/18/1966',
'POSITION' => 'ANIMAL PRODUCTION OFFICER',
'AGE' => 48,
'GRADE' => 'P4'
},
{ 'COUNTRY' => 'US',
'NAME' => 'B. Burns',
'DATE_OF_BIRTH' => '2/4/1962',
'POSITION' => 'ANIMAL PRODUCTION OFFICER',
'AGE' => 52,
'GRADE' => 'P4'
},
{ 'COUNTRY' => 'US',
'NAME' => 'R. Mung',
'DATE_OF_BIRTH' => '12/13/1968',
'POSITION' => 'ANIMAL PRODUCTION OFFICER',
'AGE' => 45,
'GRADE' => 'P4'
},
{ 'COUNTRY' => 'GERMANY',
'NAME' => 'B. Scherf',
'DATE_OF_BIRTH' => '8/31/1964',
'POSITION' => 'ANIMAL PRODUCTION OFFICER',
'AGE' => 50,
'GRADE' => 'P4'
},
{ 'COUNTRY' => 'GERMANY',
'NAME' => 'I. Hoffmann',
'DATE_OF_BIRTH' => '2/21/1960',
'POSITION' => 'CHIEF',
'AGE' => 54,
'GRADE' => 'P5'
},
];
The following is displayed:
1 ADG JUNIOR OFFICER K. King 1 DG SENIOR DIRECTOR K. King 3 P5 SENIOR OFFICER R. Forest R.Forest K. King 1 P3 JUNIOR OFFICER K. King 3 P1 FORESTRY OFFICER P. Smith T. Turner K. Turner 1 P1 GENERAL OFFICER K. King
I would like to count the number GRADES
and POSITIONS
by subsection. Here's the code I've put together so far:
#Push data read from a flat file and while loop
push @{ $grades{ $_->{GRADE} }{ $_->{POSITION} } }, $_->{NAME} for @$AG;
for my $key (
sort { substr( $a, 0, 1 ) cmp substr( $b, 0, 1 ) || substr( $b, 0, 2 ) cmp substr( $a, 0, 2 ) }
keys %grades
)
{
for my $pos ( sort { $a cmp $b } keys %{ $grades{$key} } ) {
my $names = $grades{$key}->{$pos};
my $count = scalar @$names;
print $count, ' ', $key, ' ', $pos, ' ', $names->[0], "\n";
print ' ', $names->[$_], "\n" for 1 .. $#$names;
}
}
The code will stop displaying results if there are duplicate POSITIONS and GRADES data in another department (i.e. P1, senior employee).
I don't know how to access the Hash of Hash by Division (i.e. GAGD, GAGHD, etc.) so that the same GRADE and POSITION are outputted to the division.
Here's what I really need:
**GAGD** 1 ADG JUNIOR OFFICER K. King 1 DG SENIOR DIRECTOR K. King 3 P5 SENIOR OFFICER R. Forest R.Forest K. King 1 P3 JUNIOR OFFICER K. King 3 P1 FORESTRY OFFICER P. Smith T. Turner K. Turner 1 P1 GENERAL OFFICER K. King **GAGHD** 1 P3 JUNIOR OFFICER P. Green 3 P1 FORESTRY OFFICER R. Brown F. Boo K. Church 1 P1 GENERAL OFFICER D. Peefer etc. etc.
source to share
You seem to want to hash information by subsection and then count and store names by position +. For me, it seems to work:
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my %grades = (
GAHD => [ {
NAME => 'K. Long',
POSITION => 'SENIOR OFFICER',
GRADE => 'P5'
},
{
NAME => 'J. Buber',
POSITION => 'CHIEF',
GRADE => 'D1'
},
{
NAME => 'M. Amsi',
POSITION => 'SENIOR ANIMAL HEALTH OFFICER',
GRADE => 'P5'
},
{
NAME => 'E. Xenu',
POSITION => 'SENIOR OFFICER',
GRADE => 'P5'
},
],
GAGD => [
{
NAME => 'P. Cheru',
POSITION => 'ANIMAL PRODUCTION OFFICER',
GRADE => 'P4'
},
{
NAME => 'B. Burns',
POSITION => 'ANIMAL PRODUCTION OFFICER',
GRADE => 'P4'
},
{
NAME => 'R. Mung',
POSITION => 'ANIMAL PRODUCTION OFFICER',
GRADE => 'P4'
},
{
NAME => 'B. Scherf',
POSITION => 'ANIMAL PRODUCTION OFFICER',
GRADE => 'P4'
},
{
NAME => 'I. Hoffmann',
POSITION => 'CHIEF',
GRADE => 'P5'
},
]);
for my $division (keys %grades) {
say "**$division**";
my %group;
for my $person (@{ $grades{$division} }) {
my $position = join ' ', @{ $person }{qw{GRADE POSITION}};
push @{ $group{$position} }, $person->{NAME};
}
for my $position (keys %group) {
say join ' ', scalar @{ $group{$position} },
$position,
$group{$position}[0];
my @remaining_names = @{ $group{$position} };
shift @remaining_names;
say "\t$_" for @remaining_names;
}
say q();
}
Update
If you are storing more information than the name for the person in the ref ( push push @{ $group{$position} }, [ ... ];
) array , you can get it by dereferencing each reference, for example in map
:
say join ' ', scalar @{ $group{$position} },
$position,
join "\n\t", map "@$_", @{ $group{$position} };
source to share
You are almost there with the code you have. Assuming the hash you printed out is called %grades
, I would do the following:
foreach my $g (sort keys %$grades) {
print "**$g**\n";
# put the info to be printed in a temporary hash
my %temp;
foreach (@{$grades->{$g}}) {
push @{$temp{ $_->{GRADE}." ".$_->{POSITION} }}, $_->{NAME};
}
foreach (sort keys %temp) {
# print a count of the number of names, then the grade/position info
print scalar @{$temp{$_}} . " $_ "
# @{$temp{$_}} holds the names, so just sort them and print them out.
. join("\n\t\t\t", sort @{$temp{$_}}) . "\n";
}
}
source to share