Home > Archive > PERL Beginners > May 2007 > accesing a hash of an array of hashes
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
accesing a hash of an array of hashes
|
|
|
| ive read a load of data in from a CSV file with Text::CSV and ended
up with a hash (%hash) where the keys are the column labels.
my @headings=split(/,/,$rows[0])
and then
for (my $j=1;$j<$#rows;$j++)
{
my $status = $csv->parse ($rows[$j]); # parse a CSV string into
fields
my @columns = $csv->fields (); # get the parsed fields
for (my $i=0;$i<$#columns;$i++)
{$hash{$headings[$i]}=$columns[$i];}
I want to process the data once its grouped by the date field present
in $hash. So i think I want a hash of dates where the key is that
date field
I push onto the value the hashes of the records that contain the date
push @{$Hofdates{$hash{DATE}}},\%hash;
but im having a problem working out how to access the individual
items in the hashes that are elements of the array
| |
| Paul Lalli 2007-05-26, 6:59 pm |
| On May 26, 8:17 am, pdcoo...@blueyonder.co.uk (Pauld) wrote:
> ive read a load of data in from a CSV file with Text::CSV and ended
> up with a hash (%hash) where the keys are the column labels.
> my @headings=split(/,/,$rows[0])
You're use()'ing Text::CSV, but you're not actually using Text::CSV.
Why?
> and then
>
> for (my $j=1;$j<$#rows;$j++)
Is there a reason you're ignoring the last row completely?
> {
> my $status = $csv->parse ($rows[$j]); # parse a CSV string into
> fields
Storing the status is rather pointless if you don't bother actually
checking it. . .
> my @columns = $csv->fields (); # get the parsed fields
>
> for (my $i=0;$i<$#columns;$i++)
Now you're ignoring the last column. Do you understand that $#foo is
the *last index* of @foo, and so looping until less than $#foo stops
before the last element?
> {$hash{$headings[$i]}=$columns[$i];}
Of course, that entire loop would be better served to be a hash
slice...
>
> I want to process the data once its grouped by the date field present
> in $hash. So i think I want a hash of dates where the key is that
> date field
> I push onto the value the hashes of the records that contain the date
>
> push @{$Hofdates{$hash{DATE}}},\%hash;
>
> but im having a problem working out how to access the individual
> items in the hashes that are elements of the array
You should read:
perldoc perllol
perldoc perldsc
Go step by step:
(1) %Hofdates is your overall hash.
(2) $Hofdates{'2007-05-26'} is one element of the hash. That element
is a reference to an array.
(3) @{$Hofdates{'2007-05-26'}} is the array that (2) references.
(4) ${$Hofdates{'2007-05-26'}}[0] is the first element of (3). That
element is a reference to a hash.
(5) %{${$Hofdates{'2007-05-26'}}[0]} is the hash that (4) references.
(6) ${${$Hofdates{'2007-05-26'}}[0]}{NAME} is the value of (5) at the
key 'NAME'
(7) $Hofdates{'2007-05-26'}[0]{NAME} is the same as (6), but with all
unnecessary punctuation removed. (See perldocs listed above)
Once you get used to Perl's multi-level structures, you'll find you
don't need to go through this process, as the end result just "makes
sense".
Please note, whenever possible, please try to post a short-but-
complete script that demonstrates what you're trying to do. That way
we can help you see what you might have done wrong. For example:
#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV;
use Data::Dumper;
my $csv = Text::CSV->new();
my %records_for;
my $heading = <DATA>;
$csv->parse($heading)
or die "Could not parse '$heading'. Failed on " . $csv-
>error_input;
my @heading = $csv->fields();
while (my $line = <DATA> ) {
$csv->parse($line)
or die "Could not parse line $.. Failed on " . $csv->error_input;
my @columns = $csv->fields();
my %hash;
#use a hash slice rather than a for loop.
@hash{@heading} = @columns;
push @{$records_for{$hash{DATE}}}, \%hash;
}
#Take a look at your structure.
print Dumper(\%records_for);
#Get the name of the 2nd record with date '2007-05-26';
print $records_for{'2007-05-26'}[1]{'NAME'}, "\n";
#or, get the names of all records:
foreach my $date (keys %records_for) {
my $i = 0;
foreach my $record (@{$records_for{$date}}) {
print "$date($i): $record->{NAME}\n";
$i++;
}
}
__DATA__
NAME,DATE,COLOR,SIZE
Paul,2007-05-26,blue,large
John,2007-04-21,red,small
Mary,2007-05-26,orange,medium
Peter,2006-01-30,blue,small
Hope this helps,
Paul Lalli
| |
| yaron@kahanovitch.com 2007-05-26, 6:59 pm |
|
Hi,
To access element of a given DATE (sat ... date_inp) from Hofdates you can =
do the following:
my $date_inp =3D ... ;
die "No etries for date $date_inp" unless (exists $Hofdates{$date_inp} and =
@{$Hofdates{$date_inp}});
foreach my $hash_ref (@{$Hofdates{$date_inp}}) {
while (my ($field,$value) =3D each %$hash_ref) {
print "Fieal =3D $field, value =3D $value\n";
}
}
Hope that helps
Yaron Kahanovitch
----- Original Message
-----
From: "pauld" <pdcooper@blueyonder.co.uk>
To: beginners@perl.org, perl-beginners@moderators.isc.org
Sent: 14:17:57 (GMT+0200) Africa/Harare =D7=A9=D7=91=D7=AA 26 =D7=9E=D7=90=
=D7=99 2007
Subject: accesing a hash of an array of hashes
ive read a load of data in from a CSV file with Text::CSV and ended
up with a hash (%hash) where the keys are the column labels.
my @headings=3Dsplit(/,/,$rows[0])
and then
for (my $j=3D1;$j<$#rows;$j++)
{
my $status =3D $csv->parse ($rows[$j]); # parse a CSV string into
fields
my @columns =3D $csv->fields (); # get the parsed fields
for (my $i=3D0;$i<$#columns;$i++)
{$hash{$headings[$i]}=3D$columns[$i];}
I want to process the data once its grouped by the date field present
in $hash. So i think I want a hash of dates where the key is that
date field
I push onto the value the hashes of the records that contain the date
push @{$Hofdates{$hash{DATE}}},\%hash;
but im having a problem working out how to access the individual
items in the hashes that are elements of the array
--=20
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/
| |
| yaron@kahanovitch.com 2007-05-26, 6:59 pm |
| Hi,
Be aware that you have a small problem.
Your script includes:
for (my $j=3D1;$j<$#rows;$j++)
....
for (my $i=3D0;$i<$#columns;$i++)
I think It should be=20
for (my $j=3D1;$j<=3D$#rows;$j++)
....
for (my $i=3D0;$i<=3D$#columns;$i++)
Cheers
Yaron Kahanovitch
----- Original Message -----
From: yaron@kahanovitch.com
To: "pauld" <pdcooper@blueyonder.co.uk>
Cc: "beginners" <beginners@perl.org>
Sent: 19:47:38 (GMT+0200) Africa/Harare =D7=A9=D7=91=D7=AA 26 =D7=9E=D7=90=
=D7=99 2007
Subject: Re: accesing a hash of an array of hashes
Hi,
To access element of a given DATE (sat ... date_inp) from Hofdates you can =
do the following:
my $date_inp =3D ... ;
die "No etries for date $date_inp" unless (exists $Hofdates{$date_inp} and =
@{$Hofdates{$date_inp}});
foreach my $hash_ref (@{$Hofdates{$date_inp}}) {
while (my ($field,$value) =3D each %$hash_ref) {
print "Fieal =3D $field, value =3D $value\n";
}
}
Hope that helps
Yaron Kahanovitch
----- Original Message
-----
From: "pauld" <pdcooper@blueyonder.co.uk>
To: beginners@perl.org, perl-beginners@moderators.isc.org
Sent: 14:17:57 (GMT+0200) Africa/Harare =D7=A9=D7=91=D7=AA 26 =D7=9E=D7=90=
=D7=99 2007
Subject: accesing a hash of an array of hashes
ive read a load of data in from a CSV file with Text::CSV and ended
up with a hash (%hash) where the keys are the column labels.
my @headings=3Dsplit(/,/,$rows[0])
and then
for (my $j=3D1;$j<$#rows;$j++)
{
my $status =3D $csv->parse ($rows[$j]); # parse a CSV string into
fields
my @columns =3D $csv->fields (); # get the parsed fields
for (my $i=3D0;$i<$#columns;$i++)
{$hash{$headings[$i]}=3D$columns[$i];}
I want to process the data once its grouped by the date field present
in $hash. So i think I want a hash of dates where the key is that
date field
I push onto the value the hashes of the records that contain the date
push @{$Hofdates{$hash{DATE}}},\%hash;
but im having a problem working out how to access the individual
items in the hashes that are elements of the array
--=20
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/
--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/
| |
| Mumia W. 2007-05-26, 6:59 pm |
| On 05/26/2007 07:17 AM, pauld wrote:
> ive read a load of data in from a CSV file with Text::CSV and ended
> up with a hash (%hash) where the keys are the column labels.
> my @headings=split(/,/,$rows[0])
> and then
>
> for (my $j=1;$j<$#rows;$j++)
> {
> my $status = $csv->parse ($rows[$j]); # parse a CSV string into
> fields
You don't check $status to see if the parse succeeded.
> my @columns = $csv->fields (); # get the parsed fields
>
> for (my $i=0;$i<$#columns;$i++)
> {$hash{$headings[$i]}=$columns[$i];}
>
Now %hash contains the data for the last record processed; however, data
from any previous records have been obliterated.
> I want to process the data once its grouped by the date field present
> in $hash. So i think I want a hash of dates where the key is that
> date field
> I push onto the value the hashes of the records that contain the date
>
> push @{$Hofdates{$hash{DATE}}},\%hash;
>
> but im having a problem working out how to access the individual
> items in the hashes that are elements of the array
>
>
The module Data::Dumper can help you see what's in your hash, but you
need to rethink how you initialize the first hash (%hash).
| |
| Paul Lalli 2007-05-26, 6:59 pm |
| On May 26, 1:51 pm, mumia.w.18.spam+nos...@earthlink.net (Mumia W.)
wrote:
> On 05/26/2007 07:17 AM, pauld wrote:
>
>
>
> You don't check $status to see if the parse succeeded.
>
>
>
> Now %hash contains the data for the last record processed; however, data
> from any previous records have been obliterated.
>
>
>
>
> The module Data::Dumper can help you see what's in your hash, but you
> need to rethink how you initialize the first hash (%hash).
So long as the OP declares his %hash in the for loop, there's nothing
wrong with how it's initialized. The OP did not show where he's
declaring it, unfortunately. If he made the mistake of declaring it
outside the for loop, then he's adding multiple copies of the same
hash to the array, and that single hash is going to be repeatedly
changed through the program.
Paul Lalli
| |
|
| thanks for the help - im looking up hash slices - but id like to get
something that works and then i can add new ideas etc so im going to
leave it as it for the time being.
Data::Dumper has helped sort out where an error is coming
#!/usr/bin/perl -w
use strict;
use warnings;
open O, "<$file" or die "could not open $file - $!";
undef $/;
my $whole_file = <O>;
close O;
$whole_file=~s/\x0D\x0A1/\x0D\x0AABC1/g;my @rows=split(/\x0D\x0AABC/,
$whole_file);
######splits in the correct place after looking at the CSV file with a
hex editor - ugly but it seems to work ATM######
my %hash;
my @headings=split(/,/,$rows[0])
for (my $j=1;$j<=2;$j++)
{
my @columns = $csv->fields ();
for (my $i=0;$i<=$#columns;$i++) {$hash{$headings[$i]}=$columns[$i];}
print Dumper(%hash);
push (@{$Hofdates{$hash{OPDATE}}},\%hash);
print Dumper (%Hofdates);
}
and i get this output
#############1st record
$VAR1 = 'STANTIME';
$VAR2 = '06:04';
$VAR3 = 'T3_AN2no';
$VAR4 = '';
$VAR5 = 'DESC3';
$VAR6 = '';
$VAR7 = 'T1_ANSNAME';
etc etc to the end
then
-----------------01/10/2006---BROWN---------------
$VAR1 = '01/10/2006';
$VAR2 = [
{
'STANTIME' => '06:04',
'T3_AN2no' => '',
'DESC3' => '',
'T1_ANSNAME' => 'SMITH',
'P3_STANDTIME' => '',
'T2_AN2name' => '',
<snip>
'CANCREASON' => '',
'RN2_NO' => '',
'T2_SUSDuty' => ''
}
];
#######################2nd record from file
$VAR1 = 'STANTIME';
$VAR2 = '17:39';
$VAR3 = 'T3_AN2no';
$VAR4 = '';
$VAR5 = 'DESC3';
$VAR6 = '';
<snip>
$VAR347 = 'CANCREASON';
$VAR348 = '';
$VAR349 = 'RN2_NO';
$VAR350 = '';
$VAR351 = 'T2_SUSDuty';
$VAR352 = '';
------------------01/10/2006---JONES--------------
$VAR1 = '01/10/2006';
$VAR2 = [
{
'STANTIME' => '17:39',
'T3_AN2no' => '',
'DESC3' => '',
'T1_ANSNAME' => '',
'P3_STANDTIME' => '',
'P4_OPCS4_3' => '',
'STANDATE' => '01/10/2006',
'CANCREASON' => '',
'RN2_NO' => '',
'T2_SUSDuty' => ''
},
$VAR2->[0]
];
grenada tmp #
so its pushing the second hash onto the array and overwriting the
1st
| |
| Paul Lalli 2007-05-28, 6:58 pm |
| On May 27, 4:37 pm, pdcoo...@blueyonder.co.uk (Pauld) wrote:
> thanks for the help - im looking up hash slices -
perldoc perldata
Entire arrays (and slices of arrays and hashes) are denoted
by '@', which works much like the word "these" or "those"
does in English, in that it indicates multiple values are
expected.
@days # ($days[0], $days[1],... $days[n])
@days[3,4,5] # same as ($days[3],$days[4],$days[5])
@days{'a','c'} # same as ($days{'a'},$days{'c'})
> but id like to get
> something that works and then i can add new ideas etc so im going to
> leave it as it for the time being.
>
> Data::Dumper has helped sort out where an error is coming
>
> #!/usr/bin/perl -w
> use strict;
> use warnings;
> open O, "<$file" or die "could not open $file - $!";
> undef $/;
> my $whole_file = <O>;
> close O;
> $whole_file=~s/\x0D\x0A1/\x0D\x0AABC1/g;my @rows=split(/\x0D\x0AABC/,
> $whole_file);
> ######splits in the correct place after looking at the CSV file with a
> hex editor - ugly but it seems to work ATM######
absolutely no idea what's making you think any of this is necessary.
> my %hash;
> my @headings=split(/,/,$rows[0])
> for (my $j=1;$j<=2;$j++)
> {
> my @columns = $csv->fields ();
>
> for (my $i=0;$i<=$#columns;$i++) {$hash{$headings[$i]}=$columns[$i];}
>
> print Dumper(%hash);
> push (@{$Hofdates{$hash{OPDATE}}},\%hash);
> print Dumper (%Hofdates);
>
> }
>
<output snipped>
> so its pushing the second hash onto the array and overwriting the
> 1st
No kidding. Did you read my reply? That's exactly what I said would
happen. Declare your hash in the smallest scope possible - inside the
(first) for loop. That way, when each iteration of the for loop
starts, you get a brand new hash rather than reusing the same one.
If you don't understand what that means - move the 'my %hash;' line
from where it is to just before the 'my @columns = $csv->fields ();'
line.
Paul Lalli
| |
| Jenda Krynicky 2007-05-29, 6:59 pm |
| From: pauld <pdcooper@blueyonder.co.uk>
> ive read a load of data in from a CSV file with Text::CSV and ended
> up with a hash (%hash) where the keys are the column labels.
> my @headings=split(/,/,$rows[0])
> and then
>
> for (my $j=1;$j<$#rows;$j++)
> {
> my $status = $csv->parse ($rows[$j]); # parse a CSV string into
> fields
> my @columns = $csv->fields (); # get the parsed fields
>
> for (my $i=0;$i<$#columns;$i++)
> {$hash{$headings[$i]}=$columns[$i];}
>
> I want to process the data once its grouped by the date field present
> in $hash.
Maybe your task would be easier if you used DBI and DBD::CSV and use
SQL to group the data the way you need them instead of loading the
raw data and "hand-processing" them.
Jenda
===== Jenda@Krynicky.cz === http://Jenda.Krynicky.cz =====
When it comes to wine, women and song, wizards are allowed
to get drunk and croon as much as they like.
-- Terry Pratchett in Sourcery
|
|
|
|
|