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

Fellow Monks:

I'm looking for a way to read a file with records such as follows:

ID|LastName|FirstName|TimeIN|TimeOUT 0001|Flintstone|Fred|0900|1300 0001|Flintstone|Fred|0900|1100 0001|Flintstone|Fred|1200|1630 0002|Flintstone|Wilma|0900|1500 0002|Flintstone|Fred|0930|1100 0003|Rubble|Barney|0900|1100
I would like to take these and using a hash for each unique ID, build an array of hashes within it for the other fields. I came up with the following and it does not work. I'm hoping one the enlightened ones could help me. I know that I'm just being really dense here but I spent all afternoon yesterday trying to figure it out and it never seems to quite work. Please Help..... and tell me what I'm doing wrong. Humbly yours - Mick
#load the data... while(<FILE>){ chomp; @result = split (/\|/); my @rec = ({LastName=>$result[1], FirstName=>$result[2], TimeIN=>$ +result[3], TimeOUT=>$result[4]}); $thisrecord = \@rec; push @{$ID{$result[0]}},$thisrecord; } close(FILE); #print the data foreach $key(sort keys %ID){ print "$key\n"; foreach (@{$ID{$key}}){ print "$_->{gender}\n"; } }

Replies are listed 'Best First'.
RE: Nested Data Structure Problems
by davorg (Chancellor) on Jul 27, 2000 at 19:15 UTC

    Here's something that I think does what you want. As a bonus I've made it strict-clean too.

    The changes are all in the data loading code. Each individual record that you were building was an array of one element (and that element was a reference to the hash that you really wanted). This didn't break because Perl was trying to interpret it as a pseudo-hash (does anyone else think that pseudo-hashes cause more problems than they solve?)

    Anyway, by simply removing the extra array, it all seems to work now. You could probably clean it up a bit more, but I was going for the least number of changes to your original script.

    use strict; my %ID; #load the data... while (<DATA>) { chomp; my @result = split (/\|/); my $rec = {LastName=>$result[1], FirstName=>$result[2], TimeIN=>$result[3], TimeOUT=>$result[4]}; push @{$ID{$result[0]}}, $rec; } #print the data foreach my $key(sort keys %ID){ print "$key\n"; foreach (@{$ID{$key}}){ print "$_->{LastName}\n"; } } __DATA__ 0001|Flintstone|Fred|0900|1300 0001|Flintstone|Fred|0900|1100 0001|Flintstone|Fred|1200|1630 0002|Flintstone|Wilma|0900|1500 0002|Flintstone|Fred|0930|1100 0003|Rubble|Barney|0900|1100
    --
    <http://www.dave.org.uk>

    European Perl Conference - Sept 22/24 2000, ICA, London
    <http://www.yapc.org/Europe/>
Re: Nested Data Structure Problems
by merlyn (Sage) on Jul 27, 2000 at 19:11 UTC
    I'd probably write that as:
    while (<DATA>) { chomp; my($id, $last, $first, $in, $out) = split /\|/; push @{$ID{$id}}, { LastName => $last, FirstName => $first, TimeIN => $in, TimeOUT => $out, }; }
    That'll make $ID{"0003"}[0]{"LastName"} be "Rubble". I think you were putting in an extra level of indirection that you didn't want.

    -- Randal L. Schwartz, Perl hacker

RE: Nested Data Structure Problems
by ferrency (Deacon) on Jul 27, 2000 at 19:19 UTC
    That code isn't really building an array of hashes. @rec is an array, and $thisrecord is a reference to it. So you're really building an array of arrays, each array containing possibly a single item which is a hash ref?

    You probably want something more like:

    my %rec = {LastName=>$result[1], FirstName=>$result[2], TimeIn=>$res +ult[3], TimeOUT=>$result[4]}; my $thisrecord = \%rec;
    But, you could avoid those temporary variables altogether by just doing:

    push @{$ID{$result[0]}}, {LastName=>$result[1], FirstName=>$result[2 +], TimeIN=>$result[3], TimeOUT=>$result[4]};
    use Disclaimer::Standard; # the code above is untested and may contain errors

    Alan

      Thanks, guys.... These solutions really helped a bunch! I can start working on my solution now that I understand what went wrong.

      Sheepishly,

      Mick