Perl Hash of Arrays of Arrays

8.4k Views Asked by At

I'm having some variables in my Hash of multidimensional arrays disappear as soon as I leave a for-loop. The variables are printed correctly one-by-one in the loop, but when I print any item outside of the loop, it is always empty except for the first item.

for my $y (0..$last_row - 2) {
    my $mlid = $Sheet->Cells($y+2, 1)->{'Value'}; 
    my @a = ();
    $a[0] = $Sheet->Cells($y+2, 3)->{'Value'}; 
    $a[1] = $Sheet->Cells($y+2, 4)->{'Value'}; 
    $a[2] = $Sheet->Cells($y+2, 6)->{'Value'}; 
    $a[3] = $Sheet->Cells($y+2, 7)->{'Value'}; 
    $a[4] = $Sheet->Cells($y+2, 8)->{'Value'}; 
    push @{$longHash{$mlid}}, [ @a ];
    print "Item in Array in Hash: $longHash{$mlid}[1][0]\n"; #this prints nothing

    if (exists $numPeople{$mlid}){
        $numPeople{$mlid}++;
    }else{
        $numPeople{$mlid} = 0;
        $numPeople{$mlid}++;
    }
}

print "Item in Array in Hash: $longHash{7202}[0][0]\n"; #this prints properly
print "Item in Array in Hash: $longHash{7202}[1][0]\n"; #this prints nothing

The behavior SHOULD be:

I have a hash. The key for a single mlid gives an array (representing a person). In each of the array, there should be another array, in which the 0-4 indexes are defined from an Excel file I'm reading.

So, to get the data from the mlid 7202, from the 7th person, and the 4th column in Excel, I should put $longHash{7202}[7][1] (because I mapped the 4th column to the 1th value of the array.)

2

There are 2 best solutions below

1
On BEST ANSWER

You are overwriting $longHash{$mlid} in every iteration of the loop, so in the best case $longHash{7202}[$x] will only be defined for one value of $x.

Unless you are doing something a lot more complicated than what you show here, I don't think you need to clear $longHash{$mlid} at all. Saying

$longHash{key}[index] = expression

will auto-vivify both $longHash{key} and $longhash{key}[index] without the need for any pre-initialization.

( $longHash{$mlid} = () also looks a little strange -- you are assigning an empty list to a scalar variable. I don't think it's any different from saying $longHash{$mlid}=0. If you meant to set it to an empty array reference, then you should use [] instead of ()).

2
On

I see two problems.

  1. $longHash{$mlid} = ()

You are trying to set a scalar field to a list. {$mlid}` should be set to a list ref, not a list. The simplest fix is $longHash{$mlid} = [], but that won't fix the real issue.

2. $longHash{$mlid}[$y] = [ @a ]; in combination with $longHash{$mlid} = ();

You are clearing $longHash{$mlid} on every pass thru the loop, then trying to assign to a non-zero index. Maybe what you want here is a hash of hash rather than a hash of list.

UPDATE

In your new version, I think

push @{$longHash{$mlid}}, [ @a ];

should just be

$longHash{$mlid} = [ @a ];

UPDATE 2

I think your code should be working now.

It seems to be equivalent to my simplified example below:

my %hash;

my @a;
$a[0] = "Fred";
$a[1] = 31;
$a[2] = "Melbourne";

push @{$hash{7202}}, [ @a ];

$a[0] = "Mary";
$a[1] = 25;
$a[2] = "Sydney";

push @{$hash{7202}}, [ @a ];

print $hash{7202}[0][0] . "\n";
print $hash{7202}[1][0] . "\n";

UPDATE 3

Please add

use warnings;
use strict;

to ensure that all the array indexes you think exist do exist.

And try iterating over your data to print it, e.g.

for my $mlid (keys %hash) {
    my $recordref = $hash{$mlid};
    my @record = @$recordref;
    for my $i (0..scalar(@record)-1) {
        printf "mlid %s person %d name %s\n", $mlid, $i, $record[$i][0];
    }
}