orangepeel1 has asked for the wisdom of the Perl Monks concerning the following question:

I have a hash of arrays like this:
my @array1 = ("one", "two", "three", "four", "five"); my @array2 = ("banana", "pear", "apple"); my %hash = ( numbers => \@array1, fruit => \@array2 );
I would like to use an element of the array to access the key. So for example, if I have "banana", I would like to print "fruit". However when I do  print $hash{banana} I get "use of unitialized value in print". How do I properly access this?

Replies are listed 'Best First'.
Re: Using an element in a Hash of Arrays to access the key (updated x2)
by haukex (Archbishop) on Mar 25, 2017 at 17:20 UTC

    The "direct", although inefficient approach would be to inspect the arrays and look for the values, for example using grep:

    for my $key (keys %hash) { print "$key\n" if grep {$_ eq "banana"} @{ $hash{$key} }; }

    This is inefficient because it searches the entire hash of arrays structure. It would be more efficient if you either structure your data as a hash of hashes from the beginning, or convert it. Then, it's just a matter of hash lookups, which are much faster than grepping an array. Of course, building a second data structure will eat more memory, which could be a problem if the initial data structure is large, but especially for small data structures often the speed gain will outweigh the memory cost.

    # convert my %hoh = map { $_ => { map {$_=>1} @{ $hash{$_} } } } keys %hash; # debug: inspect the converted data use Data::Dumper; print Dumper(\%hoh); # lookup my $search = "banana"; for my $key (keys %hoh) { print "$key\n" if $hoh{$key}{$search}; }

    Update: Taking this two steps further, you can build an inverse data structure, so that you don't need to do any searching, you just look up based on the string (banana). This first example applies only (!) if you know that all the values across all of the arrays are unique:

    # build inverse hash, if all values are unique my %inverse_hash; for my $key (keys %hash) { for my $val (@{ $hash{$key} }) { $inverse_hash{$val} = $key; } } # debug: inspect the converted data print Dumper(\%inverse_hash); # lookup is simple! print $inverse_hash{banana}, "\n";

    If there can be duplicate values across the arrays, then an "inverse" hash of arrays would be more appropriate:

    # convert to "inverse" hash of arrays (in two steps) my %hoa; # first, build a hash of hashes # (this eliminates duplicates at both levels) for my $key (keys %hash) { for my $val (@{ $hash{$key} }) { $hoa{$val}{$key}++; } } # then, convert the second level of hashes to arrays for my $vals (values %hoa) { $vals = [sort keys %$vals]; } # debug: inspect the converted data print Dumper(\%hoa); # lookup print join(', ', @{ $hoa{banana} } ), "\n"

    Update 2: In the last example, if you're looking up keys that may not exist, you could use the //[] trick that tybalt89 showed, as in @{ $hoa{banana} // [] }, or you could first check for the existence of the key (e.g. if (exists $hoa{banana})). (Also, in the text above I added a bit of clarification/explanation, e.g. because both the initial data structure and my final example are a hash of arrays.)

      Perfect thanks, a reverse hash is what I was looking for.
      Thank you. Could you clarify how to set the values I have in my array ("banana", "pear", "apple") into "fruit" using map? Do I need to do a foreach loop for all the values in the array?
        Could you clarify how to set the values I have in my array ("banana", "pear", "apple") into "fruit" using map?

        I updated my node just after you posted this, if I understand your question correctly, my final example (hash of arrays) should answer this, I hope?

Re: Using an element in a Hash of Arrays to access the key
by tybalt89 (Monsignor) on Mar 25, 2017 at 17:34 UTC
    #!/usr/bin/perl # http://perlmonks.org/?node_id=1185923 use strict; use warnings; my @array1 = ("one", "two", "three", "four", "five"); my @array2 = ("banana", "pear", "apple"); my %hash = ( numbers => \@array1, fruit => \@array2 ); # make a reverse lookup hash my %reverse; for my $key (keys %hash) { push @{ $reverse{$_} }, $key for @{ $hash{$key} }; } # then do direct lookup ( you may get multiple values, or none ) print "banana -> @{ $reverse{'banana'} // [] }\n";
      Thank you, this worked. Could you clarify what the purpose of  // [] is in  print "banana -> @{ $reverse{'banana'} // [] }\n";?

        That is there to handle the case when the element you are trying to look up does not exist.
        Try looking up 'peach' with and without that part of the code.

Re: Using an element in a Hash of Arrays to access the key
by johngg (Canon) on Mar 25, 2017 at 17:35 UTC

    Not answering your query, but you can save some typing when initialising your arrays by using qw (see Quote Like Operators).

    my @array1 = qw{ one two three four five }; my @array2 = qw{ banana pear apple };

    I hope this is helpful.

    Cheers,

    JohnGG

Re: Using an element in a Hash of Arrays to access the key
by choroba (Cardinal) on Mar 25, 2017 at 21:12 UTC
    You can also use hash slices to initialize the hash:
    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; my @array1 = qw( one two three four five ); my @array2 = qw ( banana pear apple ); my %hash; @hash{@array1} = ('number') x @array1; @hash{@array2} = ('fruit') x @array2; say $hash{banana};

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Using an element in a Hash of Arrays to access the key
by LanX (Saint) on Mar 25, 2017 at 17:17 UTC
     print $hash{fruit}{banana}

    update

    Oops sorry too fast, it's

    print $hash{fruit}[0]

    Since you constructed a hash of arrays

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Wouldn't this assume that I know that banana belongs to fruit already?
        Sorry seems like I misread your question.

        And haukex already gave you an exhaustive reply.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!