4

I'm trying to create a hash in which keys are contained in an array and values in an array of an array:

my @keys = (1,2,3,4,5);
my @value1 = (a,b,c,d,e);
my @value2 = (f,g,h,i,j);
my @value3 = (k,l,m,n,o);

my @values = ([@value1],[@value2],[@value3]);

my %hash;

I want to create a hash with @keys as keys, and @values as values so that key '1' would return the values a,f,k (0th element in each array) and so on.

For a single key this would be achieved as follows:

%hash=('key'=>@values);

But I'm unsure how to modify this for an array of keys.

Any help would be amazing!

Cheers,

N

2
  • 1
    %hash=('key'=>@values); is wrong. Values must be scalars. You can use %hash = ( key => \@values ); or %hash = ( key => [ @values ] );. Commented May 22, 2013 at 12:59
  • 1
    values of 0th element of each array are a,f,k not a,g,k Commented May 22, 2013 at 12:59

5 Answers 5

4

Something like this:

my %hash = map { $keys[$_] => [ $value1[$_], $value2[$_], $value3[$_] ] } 0..$#keys;

assuming that all four lists have the same length.

Sign up to request clarification or add additional context in comments.

4 Comments

Notice the 'keys' start with '1' and not '0'?
So? I did use @keys and not 0..$#keys.
And what's the first index number in a Perl array? So, the first map loop $_ will be '1' and not '0'. You'd skip 'a', 'f' and 'k' from his code example. Perhaps $value1[$_-1]?
This works well, but for each key it returns [0] for each value. For example: key: 1, Values: a f k key: 2, Values: a f k key: 3, Values: a f k Is there a way to print values[0] for key1, values[1] for key2... ?
2

I take advantage of the syntax $foo[$i][$j]; to represent your array of arrays as a two dimensional array. Here's an answer sans the map:

#! /usr/bin/env perl

use 5.12.0;
use warnings;
use Data::Dumper;

my @keys = qw(alpha beta gamma delta epsolon);

my @values1 = qw(one two three four five);
my @values2 = qw(uno dos tres quatro cinco);
my @values3 = qw(a b c d e);

my @values = ( \@values1, \@values2, \@values3 );

my %hash;
for my $item ( (0..$#keys) ) {
    my @array;
    push @array, $values[0][$item], $values[1][$item], $values[2][$item];
    $hash{$keys[$item]} = \@array;
}

say Dumper \%hash;

Here's the output:

$VAR1 = {
      'gamma' => [
           'three',
           'tres',
           'c'
         ],
      'delta' => [
           'four',
           'quatro',
           'd'
         ],
      'alpha' => [
           'one',
           'uno',
           'a'
         ],
      'beta' => [
          'two',
          'dos',
          'b'
        ],
      'epsolon' => [
             'five',
             'cinco',
             'e'
           ]
    };

Looks about right. Of course, I never verified that the various arrays are all the same size.

2 Comments

my @array = map $values[$_][$item], 0..2; to shorten a little bit.
I'm not a fan of map. I haven't found it any more efficient than doing a loop, and I find it harder to maintain. I prefer readability. It's why I created @array to use in the push instead of merely using @{ $hash{$keys[$item]} };. The latter makes my brain hurt. It's worth using @array even though it takes two extra lines of code just because it makes it easier to see what's going on. Almost every time I see map, I have to stop and think of what it's doing.
2

Try this...

#! /usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @keys = qw( undef 0 1 $key kappa );
my @value1 = qw(a b c d e);
my @value2 = qw(f g h i j);
my @value3 = qw(k l m n o);

# not used here...
# my @values = ([@value1],[@value2],[@value3]);

my %hash = map { my $key = "$keys[$_]"; $key => [ $value1[$_], $value2[$_], $value3[$_] ] } (0..$#keys);

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

print "Should print 'c': ".@{$hash{'1'}}[0]."\n";
print "Should print 'j': ".@{$hash{'kappa'}}[1]."\n";

# print Dumper( %hash );

With expected output like this:

Key: $key contains: d i n 
Key: 0 contains: b g l 
Key: 1 contains: c h m 
Key: kappa contains: e j o 
Key: undef contains: a f k 
Should print 'c': c
Should print 'j': j

Adding: If you'd like to access a single value within the hash, it should be surrounded by @{} to convert the anonymous array reference, and then ended with your index (starting from zero) in square brackets, like [0]. Examples:

print "Should print 'c': ".@{$hash{'1'}}[0]."\n";
print "Should print 'j': ".@{$hash{'kappa'}}[1]."\n";

Modified to include Ekkehard's more proper usage of (0..$#..) and added some bullet proofing of the keys.

Comments

1

Combining the best of Vedran's and Jim's solutions:

use strict;
use warnings;

# my @keys   = ( 1,  2,  3,  4,  5);
my @keys   = ( 'alpha','beta','gamma','delta','epsilon');
my @value1 = ('a','b','c','d','e');
my @value2 = ('f','g','h','i','j');
my @value3 = ('k','l','m','n','o');

my %hash = map { $keys[$_] => [ $value1[$_], $value2[$_], $value3[$_] ] } (0 .. $#keys);
printf 'first (%s) value: [%s]', $keys[0], join ", ", @{$hash{$keys[0]}};

output:

first (alpha) value: [a, f, k]

or:

first (1) value: [a, f, k]

depending on which @keys you choose.

Comments

0
use Algorithm::Loops 'MapCarE';
my @keys = qw(1 2 3 4 5);
my @value1 = qw(a b c d e);
my @value2 = qw(f g h i j);
my @value3 = qw(k l m n o);

my %hash = MapCarE { $_[0] => [ @_[1..$#_] ] } \(@keys, @value1, @value2, @value3);

MapCarE loops through the arrays, calling the code you supply first passing the first elements of the arrays, then the seconds element, etc.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.