Luigi D. Luigi D. - 5 months ago 6x
Perl Question

Double hash values in Perl

When using hashes, Perl has the very handy

keyword to handle only the values of the hash.

Say I have a double hash, i.e. something created like this:

my %dh = map { {$_->{id1}}{$_->{id2}} => $_ } @arr;

values %dh
returns an array of hashes. To access the values I would need something like this:

for my $key1 (keys %dh) {
for my $val (values %{ $dh{$key1} }) {
# stuff...

Is there a better way to do this, avoiding
cycles? Something like
values %{ values %dh }

Also, sorry for the poor syntax, I'm quite new to Perl.


You can use map again to extract it. Don't be under any illusions though - it's still looping. It might be more elegant though:

for my $val ( map { values %$_ } values %dh ) {
    #do stuff

This is more or less doing the same thing though - it's flattening your hash - getting the values from %dh - which will be a list of hash-refs - and then pulling the values out of those. (I think this should do it, but without test data I can't tell you for sure).

Note - the return order from values is undefined, so you'll get them in a randomish order. You can control ordering via keys (and maybe sort) if you're so inclined.

What map does is simply evaluate a code block, and returns the results as a list. That's why map { $_ => 1 } qw ( one two three ) works - it's actually returning a pair of values, which when you assign it to a hash is treated as key-value pairs.

use Data::Dumper;

my @stuff = map { $_ => 1 } qw ( one two three );
print Dumper \@stuff;
my %hash = map { $_ => 1 } qw ( one two three ); 
print Dumper \%hash;

So - map is returning 2 values here, which you could write as:

my @stuff = map { $_,  1 } qw ( one two three );

Because of course, => is really just a comma (with some extra functionality around quoting).

So when doing a dereference of an array, you can do the same - values returns a list of values from each element in the map.

For the sake of clarity:

use Data::Dumper;
my @stuff = ( [ 1, 2, 3 ], [4, 5, 6], [7, 8, 9] );
print Dumper \@stuff;
my @newstuff = map { @$_ } @stuff; 
print Dumper \@newstuff;

Is doing essentially the same thing - traversing each of the hash references in @stuff (because that's how multi-dimensional arrays are implemented in perl) and then 'unpacks' them, returning the results.