20

I'm fairly new to Perl, so forgive me if this seems like a simple question...

Anyway, I have a hash of arrays and I'm trying to retrieve one of the arrays in the hash, but all I can get is the scalar size of the array.

%HoA = a hash of arrays
$key = some key in the hash

foreach $nextItem (@HoA{$key}) {
  do a bunch of stuff with $nextItem
}

When I do this, $nextItem is always just the size of the array and the loop only runs through one time. I've tried printing the following:

@HoA{$key}
$HoA{$key}
@$HoA{$key}

The first two give me the scalar size and the third gives me nothing...what am I missing here?

UPDATE: I'm wondering if my problem is actually the way I'm adding the arrays to the hash. Here's what I'm doing:

@HoA{$key} = split(/ /, $list);

Does that stick the array in the hash, or the array size in the hash?

UPDATE 2: I tried the following block of code:

my $key = "TEST";
my %HoA = ();
my @testarray = (1, 2, 3);
@HoA{$key} = @testarray;
print Dumper(%HoA);

Here's the output:

$VAR1 = 'TEST';
$VAR2 = 1;

Why is it only sticking the first value of the array in?

mrplainswalker
  • 701
  • 4
  • 8
  • 26

4 Answers4

20

Try referencing your array this way:

%HoA = a hash of arrays
$key = some key in the hash

foreach $nextItem (@{$HoA{$key}}) {
  do a bunch of stuff with $nextItem
}

You take the array reference at $HoA{$key} and make it an array.

Edit: For your update, I think you will get what you want, if you do it this way:

@{$HoA{$key}} = split(/ /, $list);

or you can do

push(@{$HoA{$key}}, split(/ /, $list);

For example:

my $list = "fred joe brown sam";
my %HoA = ();
my $key = "jacob";
@{$HoA{$key} = split(/ /, $list);
foreach my $item (@{$HoA{$key}})
{
    print "Test item: $nextItem\n";
}

You will get:
Test item: fred
Test item: joe
Test item: brown
Test item: sam

Edit: Add use strict; to the top of your program. Basically you are attempting to use HoA as an array when you have a hash defined. You are referencing the contents of your hash improperly. To do it properly you really really need to have a $ between the @ and HoA. Perl is typeless and will let you get away with murder if you don't use strict;. A reference excerpt from oreilly might clear a few things up.

my @testarray is an array
my %hash is a hash
$hash{$el1} = \@array is a hash element that has the value of a ref to an array
@{$hash{$el1}} = @array is a hash element that contains an array

scrappedcola
  • 10,423
  • 1
  • 32
  • 43
  • I tried that. It just returns a size of zero and the loop doesn't even execute once. – mrplainswalker Sep 21 '12 at 18:12
  • @mrplainswalker - Try printing out the contents of the hash and ensure it has the structure you think it has. Perhaps you key is undefined or not an array like you think. You can use Data::Dumper; Dumper(%HoA) to check it out. You can also just do if(defined($HoA{$key})){print "Defined"} as a quick test also – scrappedcola Sep 21 '12 at 18:20
  • Ok, I tried the Dumper function to see what was in the hash, and you're right...it's not what I expected. Each value is just the first item in the array rather than the entire array. – mrplainswalker Sep 21 '12 at 18:40
  • @mrplainswalker - add use strict and you will see what is happening a little more. – scrappedcola Sep 21 '12 at 19:23
  • Just got back from vacation and finally got back to this. This was indeed the problem. Everything is working fine now. Thanks for the help. – mrplainswalker Sep 26 '12 at 21:53
11

Each entry in your hash is a reference to an array. For example:

$my_hash{$key}

is a reference to an array and not an array. $my_hash{$key} is merely pointing to an area in memory where that array lives. To dereference it, you put the array symbol in front:

@{ my_hash{$key} }

Things get a bit hairier when we start talking about the elements:

${ my_hash{$key} }[0]

is the first element of that array. You can imagine if that array consisted of hashes to other arrays, the syntax can get quite obtrude. Fortunately, Perl has a clean way of handling it:

$my_hash{$key}->[0];

That's exactly the same as above, but easier to understand syntax. Sometimes, it's easier to use intermediate variables just so you can refer to things without all the dereferencing:

my %hash_of_array_refs;

foreach my $key (sort keys %hash_of_array_refs) {
    my @array = @{ $hash_of_array_refs{$key} };    #Dereference
    foreach my $element (@array) {                 #Much easier to read and understand
        say "First element is $array[0]";
        say "last element is $array[$#array]";
        say "This element: $element";
    }
}

Without dereferencing you get this:

my %hash_of_array_refs;

foreach my $key (sort keys %hash_of_array_refs) {
    foreach my $element (@{ $hash_of_array_refs{$key} } ) {                 #Much easier to read and understand
        say "First element is " . $hash_of_array_refs->[0];
        say "last element is " . $hash_of_array_refs->[$#{ $hash_of_array_refs{key} } ]";
        say "This element: $element";
    }
}
David W.
  • 105,218
  • 39
  • 216
  • 337
  • +1 ~ THANK YOU!! I've looked at several articles and YOU are the first person to explain how to access an element inside a hash of arrays using $myhash{$key}->[0]. – John Henckel Apr 26 '14 at 03:19
  • @David W. suppose a hash of arrays was given. Why isn't it possible to treat each element of that hash (which is an array) like this: `foreach my $key ( keys %hash_of_arrays){ for(my $i=0; $i < 0+@hash_of_arrays{$key} ; $i++){`. Usually one could get the size of some array by doing `0+@array`. This would work if I typed an integer for the size. I think that the reason is not using `@{$hash_of_arrays{$key}}`. i.e. i have to type cast it back to array, otherwise I do something with the address. – Alexander Cska Nov 30 '22 at 09:43
3
@array

Is how you get the array, so

@{ $array_ref }

is the equivalent with an array reference.

$HoA{$key}

is a reference to an array, so

@{ $HoA{$key} }

is that array.

for my $nextItem (@{ $HoA{$key} }) 
ikegami
  • 367,544
  • 15
  • 269
  • 518
1

"Why is it only sticking the first value of the array in?"

You need to put a reference to your array into the hash by prefixing it with a backslash:

$HoA{$key} = \@testarray;

Then when you retrieve it from the hash you dereference:

@testarray = @{ $HoA{$key} };
RobEarl
  • 7,862
  • 6
  • 35
  • 50
  • True, but hasn't this already been pointed out in the other answers? I'm not clear what your answer adds here. – ire_and_curses Sep 23 '12 at 02:48
  • The other answers assumed he already had references in the hash and didn't solve the problem of trying to put an array in a hash without referencing. Other answers have since been edited. – RobEarl Sep 23 '12 at 10:19