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

#!C:\Perl64\bin\perl.exe use strict; use warnings; my $RECORD = [ { 'NAME' => 'J. Green', 'GRADE' => 'P2', 'POSITION' => 'HUMAN RES +OURCES OFFICER' }, { 'NAME' => 'P. Smith', 'GRADE' => 'P1','POSITION' => 'FORESTRY O +FFICER' }, { 'NAME' => 'T. Turner', 'GRADE' => 'P1','POSITION' => 'FORESTRY O +FFICER' }, { 'NAME' => 'K. Turner', 'GRADE' => 'P1','POSITION' => 'FORESTRY +OFFICER' }, { 'NAME' => 'R. Forest', 'GRADE' => 'P5' ,'POSITION' => 'SENIOR OF +FICER'}, { 'NAME' => 'R.Forest', 'GRADE' => 'P5' ,'POSITION' => 'SENIOR OF +FICER'}, { 'NAME' => 'K. King', 'GRADE' => 'P5','POSITION' => 'SENIOR OFF +ICER' }, { 'NAME' => 'K. King', 'GRADE' => 'P3' ,'POSITION' => 'JUNIOR OF +FICER'}, { 'NAME' => 'K. King', 'GRADE' => 'P3' ,'POSITION' => 'POLICY OF +FICER'}, { 'NAME' => 'K. King', 'GRADE' => 'P1' ,'POSITION' => 'GENERAL O +FFICER'}, { 'NAME' => 'K. King', 'GRADE' => 'DG' ,'POSITION' => 'SENIOR DI +RECTOR'}, { 'NAME' => 'K. King', 'GRADE' => 'ADG','POSITION' => 'JUNIOR OF +FICER' }, ]; my $previous_grade=''; my $previous_position=''; my %grade_count; my %position_count; my $C=0; for my $p ( sort { substr($$a{GRADE},0,1) cmp substr($$b{GRADE},0, +1) || substr($$b{GRADE},0,2) cmp substr($$a{GRADE},0,2)} @$RECORD) { #for my $p ( sort { substr($$a{GRADE},0,1) cmp substr($$b{GRADE},0, +1) || substr($$b{GRADE},0,2) cmp substr($$a{GRADE},0,2)} @{$AG{$A}}) + { my( $grade, $name,$position ) = ( $p->{'GRADE'}, $p->{'NAME'} +,$p->{'POSITION'} ); my $pp=""; my $gg=""; if ($grade eq $previous_grade && $position eq $previous_position ) { $position=" "; $gg=$grade=" "; $pp=$position; $grade_count{$previous_grade}++; $position_count{$previous_position}++; } else { $gg=$p->{'GRADE'}; $pp=$p->{'POSITION'}; $grade_count{$previous_grade}++; $position_count{$previous_position}++; } if($grade_count{$previous_grade} == 1 && $position_count{$previous_pos +ition} == 1) { $C=$grade_count{$previous_grade}; print "$C"," ",$gg," ",$pp," ",$p->{NAME},"\n"; $grade_count{$previous_grade}++; $position_count{$previous_position}++; } elsif($grade_count{$previous_grade} > 1 && $position_count{$previous_ +position} > 1) { $C=$grade_count{$previous_grade}; print $C," ",$gg," ",$pp," ",$p->{NAME},"\n"; } $previous_grade = $p->{'GRADE'}; $previous_position = $p->{'POSITION'}; }
I am having a problem resolving a counting issue. As of the time being, the code above outputs the following:
1 ADG JUNIOR OFFICER K. King 1 DG SENIOR DIRECTOR K. King 1 P5 SENIOR OFFICER R. Forest 1 R.Forest 3 K. King 4 P3 JUNIOR OFFICER K. King 1 P1 FORESTRY OFFICER P. Smith 1 T. Turner 3 K. Turner 4 P1 GENERAL OFFICER K. King
The program logic should output the following instead:
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

Every time a GRADE and POSITION are encountered the counter should increment. The total of the equal GRADEs and POSITIONs should only be output once.

Any ideas would be greatly appreciated.

Replies are listed 'Best First'.
Re: Counting Problem
by Corion (Patriarch) on Sep 10, 2014 at 11:51 UTC

    Where in your code do you output a line if the GRADE / POSITION should not be output? Maybe you can change that part of the code to behave as you want?

Re: Counting Problem
by roboticus (Chancellor) on Sep 10, 2014 at 14:01 UTC

    GuiPerl:

    You're working too hard generating lines of code, rather than letting perl do the work for you. You should read perldoc perlfaq4, and review the "Data: Hashes (Associative Arrays)" section. It should take you only a few minutes, and it'll show you a simpler way to gather the counts together.

    Also, you should keep your code neatly indented so you can see the structure. If everything is haphazardly indented, it's difficult to see the program logic, and that makes the bugs hard to see, too. Either start using an editor that can help you maintain the indentation (vim, Emacs, and a bazillion others), or periodically run your code through perltidy so you can see the code better.

    When you write a paper, you'll probably find it easier to write an outline first, so you can plan your work. Proper indentation for coding allows you to see the "outline" of what's going on inside. That'll help you code faster and better.

    <end_rant>

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      This is wonderful, however, I forgot that my data structure is setup as follows:
      my $Rec = [ {PWD=>posts_outside_pwd($rec->[5])}, {SECTION=>$rec->[14]}, {ORG_LEVEL_DESCRIP=>$rec->[10]}, {GRADE=>strip_hyphen($rec->[15])}, {POSITION=>$rec->[2]}, {NAME=>invert_name($rec->[16])}, {AGE=>convert_date_to_age(trim($rec->[17] +))}, {DATE_OF_BIRTH=>date_of_birth_cleansed($r +ec->[17])}, {COUNTRY=>$rec->[19]}, {FEMALE=>female_staff($rec->[18])}, {VACANT=>vacant_post_yellow_background($r +ec->[16])}, {GRADE_NUMS=>map{++$positions{$_}}$rec->[ +15]." ".$rec->[16]." ".$rec->[2]}, ]; push @{$AG{$rec->[10]}},$Rec; $VAR1 = 'BERF - Office of Director'; $VAR2 = [ [ { 'SECTION' => 'BERF' }, 'GRADE' => 'D1' }, { 'POSITION' => 'DIRECTOR' }, { 'NAME' => 'D. Fool' }, ];
      Each group of staff members is under a group as seen in $VAR1. This line reports an error now:
      push @{ $grades{$_->{GRADE}}{$_->{POSITION}} }, $_->{NAME} for @$AG; #How do I refer @$AG as it is a hash of hashes???
      Thanks again.

        GuiPerl:

        It looks like you munged up the stuff in the code tags, so I'm not sure. However, it looks like you've used Data::Dumper to dump your data. If so, it appears like you might've done something like:

        my %AG = ( 'BERF - Office of Director' => [ [ { SECTION => 'BERF' }, { GRADE => 'D1' }, { POSITION => 'DIRECTOR' }, { NAME => 'D. Fool' }, ] ] ); print Dumper(%AG);

        So to access any element of your data structure, start at the top, and work your way down/in to your data element. So if you want to get the GRADE, you'd need to do something like this:

        my $grade = $AG{'BERF - Office of Director'}[0][0]{GRADE};

        If you look at the data, you can see the nested pair of square brackets: That means that you have an array inside of an array. I suspect that what you *really* wanted is more like this:

        my %AG = ( 'BERF - Office of Director' => { SECTION=>'BERF', GRADE=>'D1', POSITION=>'DIRECTOR', NAME=>'D. +Fool' } );

        This would make %AG a hash of hashes, and you could access GRADE like:

        my $grade = $AG{'BERF - Office of Director'}{GRADE};

        I'd suggest taking a short break from your current project and reading perldoc perldsc, perldoc perlreftut for a little while. Let me know if you need a bit more help.

        Note: Oh, yeah, one other thing: If you're going to use Data::Dumper to print a data structure, be sure to pass a *reference* to the structure into the routine. So you'd use a statement like one of these:

        my %hash = { a=>1, b=>2 }; my $ref = \%hash; # This will give you a nice dump of your hash print Dumper($ref); # This will give you the same kind of dump print Dumper(\%hash); # This will give you a *HORRIBLE* dump: print Dumper(%hash); # It'll give you something like: $VAR1 = 'a'; $VAR2 = 1; $VAR3 = 'b'; $VAR4 = 2;

        ...roboticus

        When your only tool is a hammer, all problems look like your thumb.

Re: Counting Problem
by Athanasius (Archbishop) on Sep 10, 2014 at 13:56 UTC

    Hello GuiPerl,

    In programming, getting the right data structure is usually more than half the battle. In this case, you want to output the data sorted by (1) grade and (2) position, with each unique grade/position pair prefixed by a count of the number of persons with this grade and position. So you need a data structure that will allow you to easily access the data in the desired order and calculate the count.

    In Perl, there is a maxim: when in doubt, use a hash. What is needed here is actually a bit more complicated: a hash of hashes of arrays (HoHoA). Easier to demonstrate than to try to explain, so here is how I would begin:

    #! perl use strict; use warnings; use Data::Dump; my $RECORD = [ { NAME => 'J. Green', GRADE => 'P2', POSITION => 'HUMAN RESOURCE +S OFFICER' }, { NAME => 'P. Smith', GRADE => 'P1', POSITION => 'FORESTRY OFFIC +ER' }, { NAME => 'T. Turner', GRADE => 'P1', POSITION => 'FORESTRY OFFIC +ER' }, { NAME => 'K. Turner', GRADE => 'P1', POSITION => 'FORESTRY OFFIC +ER' }, { NAME => 'R. Forest', GRADE => 'P5', POSITION => 'SENIOR OFFICER +' }, { NAME => 'R.Forest', GRADE => 'P5', POSITION => 'SENIOR OFFICER +' }, { NAME => 'K. King', GRADE => 'P5', POSITION => 'SENIOR OFFICER +' }, { NAME => 'K. King', GRADE => 'P3', POSITION => 'JUNIOR OFFICER +' }, { NAME => 'K. King', GRADE => 'P3', POSITION => 'POLICY OFFICER +' }, { NAME => 'K. King', GRADE => 'P1', POSITION => 'GENERAL OFFICE +R' }, { NAME => 'K. King', GRADE => 'DG', POSITION => 'SENIOR DIRECTO +R' }, { NAME => 'K. King', GRADE => 'ADG', POSITION => 'JUNIOR OFFICER +' }, ]; my %grades; push @{ $grades{$_->{GRADE}}{$_->{POSITION}} }, $_->{NAME} for @$RECOR +D; dd \%grades;

    Output:

    23:51 >perl 1008_SoPW.pl { ADG => { "JUNIOR OFFICER" => ["K. King"] }, DG => { "SENIOR DIRECTOR" => ["K. King"] }, P1 => { "FORESTRY OFFICER" => ["P. Smith", "T. Turner", "K. Turner" +], "GENERAL OFFICER" => ["K. King"], }, P2 => { "HUMAN RESOURCES OFFICER" => ["J. Green"] }, P3 => { "JUNIOR OFFICER" => ["K. King"], "POLICY OFFICER" => ["K. K +ing"] }, P5 => { "SENIOR OFFICER" => ["R. Forest", "R.Forest", "K. King"] }, } 23:51 >

    Once you have the data in this form, it’s fairly straightforward to generate the desired output:

    for my $key (sort { substr($a, 0, 1) cmp substr($b, 0, 1) || substr($b, 0, 2) cmp substr($a, 0, 2) } keys %grad +es) { 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; } }

    Output:

    23:51 >perl 1008_SoPW.pl 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 1 P3 POLICY OFFICER K. King 1 P2 HUMAN RESOURCES OFFICER J. Green 3 P1 FORESTRY OFFICER P. Smith T. Turner K. Turner 1 P1 GENERAL OFFICER K. King 23:53 >

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,