tags:

views:

125

answers:

4

Hi,

I am still learning Perl scripting and need help in doing below task.

Sample data:


INV ,GOOD"
2405
INV ,BAD"
42
LOAD ,GOOD"
35588
LOAD ,BAD"
675
TRNS ,GOOD"
6
TRNS ,BAD"
122

I want to add the values under GOOD and BAD.

GOOD, <sum of all good values>
BAD, <sum of all bad values>

Can anyone please suggest me code to read the first line and put the values from next line in an array to add them latter.

+7  A: 

It could be something like this:

while (<>)
{
  $good += <> if (m/GOOD/);
  $bad += <> if (m/BAD/)
}

print "GOOD: $good";
print "BAD: $bad";

This just sums the values as you go along and prints them out, rather than accumulating them in an array. It wouldn't be too hard to change it to store the intermediate values though.

1800 INFORMATION
+1 for not slurpling
Sinan Ünür
+2  A: 
 use strict;                                     
 use warnings;                                   

 use Data::Dumper;                               

 my %to_sum;                                     
 while (<DATA>) {                                
     /,(GOOD|BAD)"$/;
     next unless $1;                            
     push @{ $to_sum{$1} ||= [] }, scalar <DATA>;
 }                                               
 chomp @$_ for values %to_sum;                   
 print Dumper \%to_sum;                          

 __DATA__                                        
 INV ,GOOD"                                      
 2405                                            
 INV ,BAD"                                       
 42                                              
 LOAD ,GOOD"                                     
 35588                                           
 LOAD ,BAD"                                      
 675                                             
 PIN ,GOOD"                                      
 6                                               
 TRNS ,BAD"                                      
 122

Gives you

 $VAR1 = {                     
           'BAD' => [          
                      '42',    
                      '675',   
                      '122'    
                    ],         
           'GOOD' => [         
                       '2405', 
                       '35588',
                       '6'     
                     ]         
         };
zakovyrya
A: 

The following puts the numbers in arrays. The parsing will be tolerant for blank lines or other input, it will wait for GOOD or BAD and then use the number from the next line.

#!/usr/bin/perl -w
use strict;
use warnings;

my $state = 0;
my $state_good = 1;
my $state_bad  = 2;
my @goods = ();
my @bads = ();


while (my $line = <>) {
        chomp($line);

        if ($line =~ /GOOD/) {
                $state = $state_good;
                next;
        }
        if ($line =~ /BAD/) {
                $state = $state_bad;
                next;
        }
        next if $state == 0;

        if ($state == $state_good) {
                push @goods, $line;
                $state = 0;
        }
        if ($state == $state_bad) {
                push @bads, $line;
                $state = 0;
        }
}


print "sum good: ", sum(@goods), "\n";
print "sum bad:  ", sum(@bads),  "\n";

sub sum {
        my $ret = 0;
        while (@_) {
                my $n = shift;
                $ret += $n;
        }
        return $ret;
}
hlovdal
A comment on why this was rated -1 (as well as suggestions for improvement) would be appreciated.
hlovdal
I didn't downvoted, but it seems like a highly specialized solution when the problem calls for a more generic one.
Leonardo Herrera
Hm, that was a very unexpected interpretation. I did by all means intend to write in a generic way.I often use state variables when parsing input where I want to parse differently depending the content of previous linesand I consider that to be a very generic way to do it (although the code of course becomes a specific implementation).This is loosely similar to Start conditions in lex (http://dinosaur.compilertools.net/flex/flex_11.html#SEC11).
hlovdal
I thought it was a perfectly cromulent solution to the problem. Obviously it is written with a completely different philosophy to the other solutions, but there are no obvious errors so the downvotes were a bit unwarranted.
1800 INFORMATION
It is likely that the answer is difficult to read, since it contains no comments although it is long. I am also not sure about what you are doing with the last function. - I would also change the variable names to be more descriptive, for instance, $ret to $result. -- I am not sure about the purposes of the following variables/functions: sub, my, $_, $n and shift.
Masi
Thank you for the feedback. Sub, my and shift are perl keywords, @_ is the array of arguments to the function. The variable name $n could probably been $number for more clarity (although I might just as well have written $ret += shift).
hlovdal
For an example that the code in this answer is not non-generic, seehttp://stackoverflow.com/questions/1269346/1269368#1269368 where I used this code as a template for writing an answer to that question (currently rated +2).
hlovdal
+4  A: 
#!/usr/bin/perl

use strict;
use warnings;

my $bad = 0;
my $good = 0;
while (<>) {
    if ( /,GOOD"$/ ... /^(\d+)$/ ) {
        $good += $1 if defined($1);
    }
    if ( /,BAD"$/ ... /^(\d+)$/ ) {
        $bad += $1 if defined($1);
    }
}

print "GOOD, $good\n";
print "BAD, $bad\n";

Using the range operator in scalar context is explained in man perlop (Range Operator), and it's quite cool. Got the answer from the Perl Cookbook, recipe 6.8.

Robin Smidsrød