Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Printing values for hash of arrays in perl

I've got a hash of arrays that's declared as follows:

my %hash;
push @{ $hash{ $value1[$_] } }, [ $value1[$_], $value2[$_], $value3[$_], $value4[$_], $value5[$_] ] for 0 .. $#value1;

I want to be able to inspect the values for each key using:

open KEYS, '>keys.txt' or die "Can't write to 'keys.txt'\n";

for my $key ( sort keys %hash ) {
    print KEYS "Key: $key contains the values: ";
    for my $value ( @{$hash{$value1}} ) {
        print KEYS "$value ";
    }   
    print KEYS "\n";
}
close(KEYS);

While I can visualise the keys and associated values using Data::Dumper, the output from the above code gives memory locations, rather than values, for each key. E.g:

Key: 'Value1' contains the values: ARRAY(0x7fcd8645ba68) 

Even though I'm pushing the same number of values onto each array, each key contains different numbers of values

Is there something wrong with the way I'm going about this?

like image 652
fugu Avatar asked Jan 14 '23 01:01

fugu


2 Answers

First, in your inner loop, you have

for my $value ( @{$hash{$value1}} ) {
    print KEYS "$value ";
}   

What on earth is $value1? I think you wanted to use $key. Always use strict; use warnings to get warned about undefined values and undeclared variables.

Next, let's take a look what happens when we do

my %hash;
push @{ $hash{ $value1[$_] } }, "(value$_)" for 0 .. $#value1;

instead, i.e. we just push a string onto the arrayref in the hash. Then, the output looks somewhat like

Key: Value1 contains the values: (value0)
Key: Value2 contains the values: (value1)
Key: Value3 contains the values: (value2)

Aha! Whatever we push onto that arrayref is printed out as is. If you push an anonymous arrayref like [...], you get the stringification of that reference: ARRAY(0x1234567).

You probably want the contents of that arrayref. Easy: just dereference it.

...;
print KEYS "[@$value] ";

or something like that. The "[...]" are used here just to visually group the output.


Style notes:

Please consider 3-arg open with lexical filehandles:

my $filename = "keys.txt";
open my $keys, "<", $filename or die "Can't open $filename: $!";

or use automatic error handling:

use autodie;
open my $keys, "<", "keys.txt";

Either way, it is usually important that you include the reason for the failure $! in the error message, or it is almost useless.

Instead of using loop, your code may be more elegant with map and join, depending on your taste. I would have probably written the loop as

use feature 'say';
for my $key ( sort keys %hash ) {
    say {$keys} "Key: $key contains the values: "
      . join " ", map { "[@$_]" } @{ $hash{$key} };
}
like image 170
amon Avatar answered Jan 26 '23 00:01

amon


That's because you're pushing an arrayref instead of a list of values.

Compare

push @array, [ $foo ];

to

push @array, $foo;

Both @arrays contain a single element, but the former holds an arrayref containing $foo, whereas the latter holds just $foo.

If you lose the square braces after the first push argument you'll be good to go.

like image 45
Zaid Avatar answered Jan 26 '23 01:01

Zaid