Perl process and store unique values in array - perl

Below is the log file content and I'm reading the log file and grouping it based on the string - JIRA.
JIRA: COM-1234
Program:Development
Reviewer:John Wick
Description:Genral fix
rev:r345676
------------------------------------------
JIRA:COM-1234
Program:Development
Reviewer:None
Description:Updating Received
rev:r909276
------------------------------------------
JIRA: COM-6789
Program:Testing
Reviewer:Balise Mat
Description:Audited
rev:r876391
------------------------------------------
JIRA: COM-6789
Program:Testing
Reviewer:Chan Joe
Description:SO hwat
rev:r698392
------------------------------------------
JIRA: COM-6789
Program:Testing
Reviewer:Chan Joe
Description:Paid the Due
rev:r327896
------------------------------------------
My requirement is , iterate thru every unique JIRA value - COM-1234 , COM-6789, etc and store the following or immediate details in to individual array like
(for COM-1234)
#prog = Development;
#Reviewer = John Wick;
#Description = Genral fix;
#rev = r345676;
(for COM-6789)
#prog = Testing;
#Reviewer = Balise Mat;
#Description = Audited;
#rev = r876391;
If the JIRA value is identical , say COM-1234 repeated 2 times and COM-6789 for 3 times , still push only the following or immediate details to the respective arrays. (i.e. values of the keys 'Program','Reviewer' ....)
(COM-1234)
#prog = Development;
#Reviewer = None;
#Description = Updating Received ;
#rev = r909276;
I'm very new to Perl and I can manage to reach only for the unique values and not sure how to push the following values to individual arrays.
Any inputs will be really helpful. Thanks.
My incomplete code:
#!/usr/bin/perl
use warnings;
use Data::Dumper;
$/ = "%%%%";
open (AFILE, ""<", ""D:\\mine\\out.txt");
while (<AFILE>)
{
#temp = split(/-{20,}/, $_);
}
close (AFILE);
my %jiraHash;
for ($i=0; $i<=#temp; $i++) {
if (($temp[$i] =~ /(((JIRA|SVN)\s{0,1}:(\s{0,2}[A-Za-z0-9-\s]{4,9}),
{0,1}\s{0,2}){1,5})\nProgram\s{0,1}:\s{0,2}Development/) ||
($temp[$i] =~ /(((JIRA|SVN):(\s{0,2}[A-Za-z0-9-\s]{4,9}),
{0,1}\s{0,2}){1,5})\nProgram\:\s{0,2}Testing/)) {
$jiraId = $2;
$jiraId =~ s/JIRA\s*\://;
$temp[$i] =~ s/\w{3,}\s?:\s?//g;
#print "==>$jiraId\n";
$jiraHash{$jiraId} = $temp[$i];
} else {
#print "NOT\n";
}
}
print Dumper(%jiraHash);
I'm planning display as HTML report in below format
Program: Development
FOR ID: COM-1234
Revision Reviewer Comment
r345676 John Wick Genral fix
Revision Reviewer Comment
r909276 None Updating Received
Program: Testing
FOR ID: COM-6789
Revision Reviewer Comment
r876391 Balise Mat Audited
Revision Reviewer Comment
r698392 Chan Joe SO hwat
Revision Reviewer Comment
r327896 Chan Joe Paid the Due

It sounds like this data should be in a database.
But it is relatively simple to parse it into a data structure. Here, I've gone for a hash where the key is the Jira identifier and the value is a reference to an array that contains hash references. Each of the referenced hashes contains the details from one of the records.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Data::Dumper;
my #records = do {
local $/ = '------------------------------------------';
<>;
};
chomp #records;
my %jira;
foreach (#records) {
next unless /\S/;
my %rec = /^(\w+):\s*(.+?)$/mg;
push #{$jira{$rec{JIRA}}}, \%rec;
}
say Dumper \%jira;
When you run it on your given data, you get this output:
$VAR1 = {
'COM-6789' => [
{
'Program' => 'Testing',
'JIRA' => 'COM-6789',
'rev' => 'r876391',
'Reviewer' => 'Balise Mat',
'Description' => 'Audited'
},
{
'Program' => 'Testing',
'JIRA' => 'COM-6789',
'rev' => 'r698392',
'Reviewer' => 'Chan Joe',
'Description' => 'SO hwat '
},
{
'Program' => 'Testing',
'JIRA' => 'COM-6789',
'rev' => 'r327896',
'Reviewer' => 'Chan Joe',
'Description' => 'Paid the Due'
}
],
'COM-1234' => [
{
'Program' => 'Development',
'JIRA' => 'COM-1234',
'rev' => 'r345676',
'Reviewer' => 'John Wick ',
'Description' => 'Genral fix'
},
{
'Program' => 'Development',
'JIRA' => 'COM-1234',
'rev' => 'r909276',
'Reviewer' => 'None',
'Description' => 'Updating Received '
}
]
};
From there, it's relatively simple to get a display of the data:
foreach my $j (keys %jira) {
say "JIRA: $j";
foreach (#{$jira{$j}}) {
say "Program: $_->{Program}";
say "Revision: $_->{rev}";
# etc...
}
}

Since your data is nicely structured into one line per data item, and since Perlby default processes input line by line, I suggest doing that instead of messing with $/ or regexps to split the input records. This does require you to remember the JIRA issue ID from the first line of each record, but that's simple — just store it in a variable declared outside the loop, like this:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper qw(Dumper);
my %records;
my $jiraID;
while (<>) {
chomp;
if (/^JIRA: (.*)/ and not defined $jiraID) {
$jiraID = $1;
$records{$jiraID} = {}; # wipe out any old data for this ID
} elsif (/^(Program|Reviewer|Description|rev):(.*)/ and defined $jiraID) {
$records{$jiraID}{$1} = $2;
} elsif (/^-{20,}$/) {
undef $jiraID; # end of record
} else {
die qq(Unexpected input line "$_");
}
}
print Dumper(\%records);
The code above reads its input from any file(s) provided as command line arguments, or from standard input if there aren't any, using the <> default input operator. If you want to read from a specific file handle that you've opened yourself, you can of course provide one.
Note that the code above stores only the last record for each ID. If you want to store all of them in an array, replace the line:
$records{$jiraID} = {}; # wipe out any old data for this ID
with:
push #{$records{$jiraID}}, {}; # start new record for this ID
and change the line:
$records{$jiraID}{$1} = $2;
to:
$records{$jiraID}[-1]{$1} = $2;
Ps. The regexps in the code above are based on your sample data. If your real data has other types of lines in it (or variations e.g. in the amount of whitespace), you'll need to adjust them to match those lines too. I coded the script to die if it sees anything unexpected, so it's easy to tell if that happens.
Update: Based on the sample output you posted while I was writing this answer, it looks like you want to group the data by both the JIRA and the Program lines. That's easy enough to do as well, e.g. like this:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper qw(Dumper);
my %records;
my $jiraID, $progID;
while (<>) {
chomp;
if (/^JIRA:\s*(.*)/ and not defined $jiraID) {
$jiraID = $1;
} elsif (/^Program:\s*(.*)/ and defined $jiraID and not defined $progID) {
$progID = $1;
push #{$records{$jiraID}{$progID}}, {}; # start new record for these IDs
} elsif (/^(Reviewer|Description|rev):(.*)/ and defined $progID) {
$records{$jiraID}{$progID}[-1]{$1} = $2;
} elsif (/^-{20,}$/) {
undef $jiraID, $progID; # end of record
} else {
die qq(Unexpected input line "$_");
}
}
print Dumper(\%records);
Note that I grouped the output data structure first by the JIRA ID and then by the program, but of course those would be easy to swap (or even combine into a single hash key, if you prefer).

This doesn't handle the final output, but this may be a simplified approach to storing lists of lists for each hash element (ticket id), along with some sample output at the end. It's not formatted the way you want, but that should be easy enough:
use strict;
my (%jira, #values, $ticket_id);
open my $IN, '<', 'jira.txt' or die;
while (<$IN>) {
chomp;
my ($key, $val) = split /:\s*/;
if ($key eq 'JIRA') {
if (#values) {
push #{$jira{$ticket_id}}, [ #values ];
#values = ();
}
$ticket_id = $val;
} elsif ($key eq 'Program') {
$values[0] = $val;
} elsif ($key eq 'Reviewer') {
$values[1] = $val;
} elsif ($key eq 'Description') {
$values[2] = $val;
} elsif ($key eq 'rev') {
$values[3] = $val;
}
}
close $IN;
push #{$jira{$ticket_id}}, [ #values ];
while (my ($ticket, $ref) = each %jira) {
print "$ticket =>\n";
foreach my $line_ref (#$ref) {
print join "\t", #$line_ref, "\n";
}
}
Sample output:
COM-1234 =>
Development John Wick Genral fix r345676
Development None Updating Received r909276
COM-6789 =>
Testing Balise Mat Audited r876391
Testing Chan Joe SO hwat r698392
Testing Chan Joe Paid the Due r327896

Related

array of hashes example fails

I have a problem with multi dimension arrays. I then tried a sample in the book Perl 4th edition, page 379, and that failed as well! Why?
#!/usr/bin/perl
use strict;
use warnings;
# example in manual page 379
# input from file containing: husband=fred pal=barney wife=wilma pet=dino
while ( <> ) {
next unless s/^(.*?):\s*//;
my $who = $1;
for my $field ( split ) {
(my $key, my $value) = split /=/, $field;
my $HoH{$who}{$key} = $value;
}
}
`
Useful trick for illustrative examples - you can in line __DATA__ at the end of your file, and use that.
Anyway, when I run your code, I get:
Global symbol "$key" requires explicit package name (did you forget to declare "my $key"?)
Global symbol "$value" requires explicit package name (did you forget to declare "my $value"?
You are also declaring %HoH badly - you shouldn't use that form, and instead:
my %HoH;
And also that regex - will skip your input text, because it's looking for : and your input doesn't contain any. I will assume that like should be prefixed with flintstone:.
So to simplify and give you something that works:
#! usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %HoH;
while (<DATA>) {
next unless s/^(.*?):\s*//;
my $who = $1;
for my $field (split) {
my ( $key, $value ) = split /=/, $field;
$HoH{$who}{$key} = $value;
}
}
print Dumper \%HoH;
__DATA__
flintstone: husband=fred pal=barney wife=wilma pet=dino
This outputs the dumped HoH:
$VAR1 = {
'flintstone' => {
'husband' => 'fred',
'wife' => 'wilma',
'pal' => 'barney',
'pet' => 'dino'
}
};
Your code contains some syntax errors, and your input text is wrong (there is no :
Try this:
my %HoH;
while ( <DATA> ) {
next unless s/^(.*?):\s*//;
my $who = $1;
for my $field ( split ) {
my ($key, $value) = split /=/, $field;
$HoH{$who}{$key} = $value;
}
}
print Dumper \%HoH;
__DATA__
flintstones: husband=fred pal=barney wife=wilma pet=dino

String Parsing for nested parenthesis in perl

The issue is when I try to compare the input to the output file, i am unable to handle the nesting of the parenthesis, and the complexity needs to be very low. is there a parsing module for this? compatible to 5.8.4. I found modules but they needed at least 5.10.:(
Input
(K1=V1,K2=V2,K3=V3(K2=V2.K5=V5)K6=V6(K7=V7,K8=V8(K9=V9,K10=V10)K11=V11)K12=V12,K13=V13)
OUTPUT FILE
(K0=V0,K1=V1,K2=V2,K3=V3(K1=V1,K2=V2,K4=V4,K5=V5,K14=V14),K15=V15,K6=V6(K18=V18,K7=V7,K19=V19,K8=V8(K20=V20,K9=V9,K16=V16,K10=V10,K21=V21)K11=V11)K12=V12,K13=V13,K22=V22)
I need to pick up each key value pair from input and one by one verify from the output file that the value is the same. if not
I need to store the key with the existing value.( The issue is with the nesting )
INPUT
K3=V3(K2=V2,K5=V5)
OUTPUT
K3=V3(K1=V1,K2=V2,K4=V4,K5=V5,K14=V14)
The issue is that "K2=V2" inside the V3 value is to be checked inside the V3 value in the output file. So I cannot just use a regular expression to do that as K2=V2 may appear outside the V3 parenthesis too.
I was trying to create a hash of a hash of a hash but failed. could someone suggest a way I could achieve this?
The following code builds the hash of hashes. Note that values (V3) are lost if they contain an inner hash.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub to_hash {
my $string = shift;
$string =~ s/^\( | \)$//gx; # Remove the outer parentheses.
my #stack = {};
my #keys;
while (length $string) {
$string =~ s/^([^,=()]+) = ([^(),]*)//x or die $string;
my ($key, $value) = ($1, $2);
$stack[-1]{$key} = $value;
next if $string =~ s/^,//;
if ($string =~ s/^\(//) {
push #stack, {};
push #keys, $key;
} elsif ($string =~ s/^\),?//) {
my $last = pop #stack;
$stack[-1]{ pop #keys } = $last;
}
}
return $stack[0]
}
my $input = '(K1=V1,K2=V2,K3=V3(K2=V2,K5=V5)K6=V6(K7=V7,K8=V8(K9=V9,K10=V10)K11=V11)K12=V12,K13=V13)';
print Dumper to_hash($input);
Output
$VAR1 = {
'K2' => 'V2',
'K13' => 'V13',
'K6' => {
'K7' => 'V7',
'K8' => {
'K9' => 'V9',
'K10' => 'V10'
},
'K11' => 'V11'
},
'K3' => {
'K2' => 'V2',
'K5' => 'V5'
},
'K12' => 'V12',
'K1' => 'V1'
};
Nested parens either suggests an application of Text::Balanced and its extract_bracketed function, or building yourself a little parser subclass on Parser::MGC. Using the latter to build a little "convert string into data structure" parser is usually pretty straightforward for simple examples like this.

Push into end of hash in Perl

So what I am trying to do with the following code is push a string, let's say "this string" onto the end of each key in a hash. I'm completely stumped on how to do this. Here's my code:
use warnings;
use strict;
use File::Find;
my #name;
my $filename;
my $line;
my #severity = ();
my #files;
my #info = ();
my $key;
my %hoa;
my $xmlfile;
my $comment;
my #comments;
open( OUTPUT, "> $ARGV[0]" );
my $dir = 'c:/programs/TEST/Test';
while ( defined( $input = glob( $dir . "\\*.txt" ) ) ) {
open( INPUT, "< $input" );
while (<INPUT>) {
chomp;
if (/File/) {
my #line = split /:/;
$key = $line[1];
push #{ $hoa{$key} }, "Filename\n";
}
if ( /XML/ ... /File/ ) {
$xmlfile = $1;
push #{ $hoa{$key} }, "XML file is $xmlfile\n";
}
if (/Important/) {
push #{ $hoa{$key} }, "Severity is $_\n";
}
if (/^\D/) {
next if /Important/;
push #{ $hoa{$key} }, "Given comment is $_\n";
}
push #{ $hoa{$key} }, "this string\n";
}
}
foreach my $k ( keys %hoa ) {
my #list = #{ $hoa{$k} };
foreach my $l (#list) {
print OUTPUT $l, "\n";
}
}
}
close INPUT;
close OUTPUT;
Where I have "this string" is where I was trying to push that string onto the end of the array. However, what ended up happening was that it ended up printing "this string" three times, and not at the end of every key like I wanted. When I tried to put it outside the while() loop, it said that the value of $key was not initialized. So please, any help? And if you need any clarification on what I'm asking, just let me know. Thank you!
No offence, but there are so many issues in this code I don't even know where to start...
First, the 'initialization block' (all these my $something; my #somethings lines at the beginning of this script) is not required in Perl. In fact, it's not just 'redundant' - it's actually confusing: I had to move my focus back and forth every time I encountered a new variable just to check its type. Besides, even with all this $input var is still not declared as local; it's either missing in comments, or the code given has omissions.
Second, why do you declare your intention to use File::Find (good) - but then do not use it at all? It could greatly simplify all this while(glob) { while(<FH>) { ... } } routine.
Third, I'm not sure why you assign something to $key only when the line read is matched by /File/ - but then use its value as a key in all the other cases. Is this an attempt to read the file organized in sections? Then it can be done a bit more simple, either by slurp/splitting or localizing $/ variable...
Anyway, the point is that if the first line of the file scanned is not matched by /File/, the previous (i.e., from the previous file!) value is used - and I'm not quite sure that it's intended. And if the very first line of the first file is not /File/-matched, then an empty string is used as a key - again, it smells like a bug...
Could you please describe your task in more details? Give some test input/output results, perhaps... It'd be great to proceed in short tasks, organizing your code in process.
Your program is ill-conceived and breaks a lot of good practice rules. Rather than enumerate them all, here is an equivalent program with a better structure.
I wonder if you are aware that all of the if statements will be tested and possibly executed? Perhaps you need to make use of elsif?
Aside from the possibility that $key is undefined when it is used, you are also setting $xmlfile to $1 which will never be defined as there are no captures in any of your regular expressions.
It is impossible to tell from your code what you are trying to do, so we can help you only if you show us your output, input and say how to derive one from the other.
use strict;
use warnings;
use File::Find;
my ($outfile) = #ARGV;
my $dir = 'c:/programs/TEST/Test';
my %hoa;
my $key;
while (my $input = glob "$dir/*.txt") {
open my $in, '<', $input or die $!;
while (<$in>) {
chomp;
if (/File/) {
my $key = (split /:/)[1];
push #{ $hoa{$key} }, "Filename\n";
}
if (/XML/ ... /File/) {
my $xmlfile = $1;
push #{ $hoa{$key} }, "XML file is $xmlfile\n";
}
if (/Important/) {
push #{ $hoa{$key} }, "Severity is $_\n";
}
if (/^\D/) {
next if /Important/;
push #{ $hoa{$key} }, "Given comment is $_\n";
}
push #{ $hoa{$key} }, "this string\n";
}
close $in;
}
open my $out, '>', $outfile or die $!;
foreach my $k (keys %hoa) {
foreach my $l (#{ $hoa{$k} }) {
print $out $l, "\n";
}
}
close $out;
I suspect based on your code, that the line where $key is set is not called each time through the loop, and that you do not trigger any of the other if statements.
This would append "this string" to the end of the array. Based on that you are getting 3 of the "this strings" at the end of the array, I would suspect that two lines do not go through the if (/FILE/) or any of the other if statements. This would leave the $key value the same and at the end, you would append "this string" to the array, using whatever the last value of $key was when it was set.
This will append the string "this string" to every element of the hash %hoa, which elements are array refs:
for (values(%hoa)) { push #{$_}, "this string"; }
Put that outside your while loop, and you'll print "this string" at the end of each element of %hoa.
It will autovivify array refs where it finds undefined elements. It will also choke if it cannot dereference an element as an array, and will manipulate arrays by symbolic reference if it finds a simple scalar and is not running under strict:
my %autoviv = ( a => ['foo'], b => undef );
push #$_, "PUSH" for values %autoviv; # ( a => ['foo', 'PUSH'], b => ['PUSH'] )
my %fatal = ( a => {} );
push #$_, "PUSH" for values %fatal; # FATAL: "Not an ARRAY reference at..."
my %dangerous = (a => "foo");
push #$_, "PUSH" for values %dangerous; # Yikes! #foo is now ("PUSH")
use strict;
my %kablam = (a => "foo");
push #$_, "PUSH" for values %kablam; # "Can't use string ("foo") as an ARRAY ref ..."
As I understand it, traverse the hash with a map command to modify its keys. An example:
EDIT: I've edited because I realised that the map command can be assigned to the same hash. No need to create a new one.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my %hash = qw|
key1 value1
key2 value2
key3 value3
|;
my %hash = map { $_ . "this string" => $hash{ $_ } } keys %hash;
print Dump \%hash;
Run it like:
perl script.pl
With following output:
$VAR1 = {
'key3this string' => 'value3',
'key2this string' => 'value2',
'key1this string' => 'value1'
};

Dealing with multiple capture groups in multiple records

Data Format:
attribname: data
Data Example:
cheese: good
pizza: good
bagel: good
fire: bad
Code:
my $subFilter='(.+?): (.+)';
my #attrib = ($dataSet=~/$subFilter/g);
for (#attrib)
{
print "$_\n";
}
The code spits out:
cheese
good
pizza
good
[etc...]
I was wondering what an easy Perly way to do this is? I am parsing the data from a log the data above is trash for simplicity. I am newer to Perl, I suspect I could do this via fanangling indexes, but I was wondering if there is a short method of implementing this? Is there any way to have the capture groups put into two different variables instead of serially appended to the list along with all matches?
Edit: I want the attribute and it's associated value together so I can the do what I need to to them. For example if within my for loop I could access both the attribute name and attribute value.
Edit:
I tried
my %attribs;
while (my $line = <$data>)
{
my ($attrib, $value) = ($line=~m/$subFilter/);
print $attribs{$attrib}," : ", $value,"\n";
}
and no luck :( I don't get any output with this. My data is in a variable not a file, because it parsed out of a set of parent data which is in a file. It would be convenient if the my variable worked so that my (#attrib, #value) = ($line=~/$subFilter/g); filled the lists appropriately with the multiple matches.
Solution:
my #line = ($7 =~/(.+?)\n/g);
for (#line)
{
my ($attrib, $value) = ($_=~m/$subFilter/);
if ($attrib ne "")
{
print $attrib," : ", $value,"\n";
}
}
I'm not really clear on what you actually want to store, but here's how you could store the data in a hash table, with '1' indicating good and '0' indicating 'bad':
use strict;
use warnings;
use Data::Dumper;
my %foods;
while (my $line = <DATA>)
{
chomp $line;
my ($food, $good) = ($line =~ m/^(.+?): (.+)$/);
$foods{$food} = ($good eq 'good' ? 1 : 0);
}
print Dumper(\%foods);
__DATA__
cheese: good
pizza: good
bagel: good
fire: bad
This prints:
$VAR1 = {
'bagel' => 1,
'cheese' => 1,
'fire' => 0,
'pizza' => 1
};
A sensible approach would be to make use of the split function:
my %attrib;
open my $data, '<', 'fileName' or die "Unable to open file: $!";
while ( my $line = <$data> ) {
my ( $attrib, $value ) = split /:\s*/, $line, 2;
$attrib{$attrib} = $value;
}
close $data;
foreach my $attrib ( keys %attrib ) {
print "$attrib: $attrib{$attrib}\n";
}
If you're into one-liners, the following would achieve the same:
$ perl -F/:\s*/ -ane '$attrib{$F[0]} = $F[1]; } END { print $_,"\t",$attrib{$_},"\n" foreach keys %attrib;" fileName

Mapping values with Column header and row header

I have some files with below data.
sample File 1:
sitename1,2009-07-19,"A1",11975,17.23
sitename1,2009-07-19,"A2",11,0.02
sitename1,2009-07-20,"A1",2000,17.23
sitename1,2009-07-20,"A2",538,0.02
I want to map the values in column 4 with column 2 and 3 as shown below.
Output required.
Site,Type,2009-07-19,2009-07-20
sitename1,"A1",11975,2000
sitename1,"A2",11,538
Here is what I have tried so far:
#! /usr/bin/perl -w
use strict;
use warnings;
my $column_header=["Site,Type"];
my $position={};
my $last_position=0;
my $current_event=[];
my $events=[];
while (<STDIN>) {
my ($site,$date,$type,$value,$percent) = split /[,\n]/, $_;
my $event_key = $date;
if (not defined $position->{$event_key}) {
$last_position+=1;
$position->{$event_key}=$last_position;
push #$column_header,$event_key;
}
my $pos = $position->{$event_key};
if (defined $current_event->[$pos]) {
dumpEvent();
}
if (not defined $current_event->[0]) {
$current_event->[0]="$site,$type";
}
$current_event->[$pos]=$value;
}
dumpEvent();
my $order = [];
for (my $scan=0; $scan<scalar(#$column_header); $scan++) {
push #$order,$scan;
}
printLine($column_header);
map { printLine($_) } #$events;
sub printLine {
my $record=shift;
my #result=();
foreach my $offset (#$order) {
if (defined $record->[$offset]) {
push #result,$record->[$offset];
} else {
push #result,"";
}
}
print join(",",#result)."\n";
}
sub dumpEvent {
return unless defined $current_event->[0];
push #$events,$current_event;
$current_event=[];
}
The output i am getting is as below.
*Site,Type,2009-07-19,2009-07-20*
sitename1,"A1",11975,
sitename1,"A2",11,
sitename1,"A1",,14620
sitename1,"A2",,538
If I understand you correctly (and I have to admit I'm only guessing), you have several types of things at different dates and a value for each. Thus you need a data structure like this hash for each site:
$foo = {
site => 'sitename1',
type => 'A1',
dates => [
{
date => '2009-07-19',
value => 11975,
},
{
date => '2009-07-20',
value => 538,
},
],
};
Is that even close?
The folowing code produces the expected result and makes "some" sense. I don't know if it makes real sense.
my %dates;
my %SiteType;
while (<DATA>) {
chomp;
my ($site,$date,$type,$value,$percent) = split /,/;
$dates{$date} = '1';
push #{$SiteType{"$site,$type"}}, $value ;
};
print 'Site,Type,', join(',', sort keys %dates), "\n";
foreach ( sort keys %SiteType) {
print $_, ',', join(',', #{$SiteType{$_}}), "\n";
};