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

Dear all,

I need your help to extract hash keys for values stored in an array (or a scalar).

In my example the hash keeps changing in a loop and so does the the array )or scalar. For example, %hash={a=>2,b=>3,c=>3}, and @array=(3) or even $scalar=3 (since array is really one element, it can be a scalar as easily); I want the script to return either b or c as keys to the value of 3 (b OR c are equally acceptable answers, but NOT both)

I am a newbie to PERL programming, so I am familiar only with using keys, values and each for hashes, but I am not sure how to extract a key given the value in a separate scalar or array.

Thanks...
  • Comment on Extract hash keys for values stoted in array

Replies are listed 'Best First'.
Re: Extract hash keys for values stoted in array
by BrowserUk (Patriarch) on May 01, 2011 at 10:42 UTC

    Based on the little you've told us, you could do:

    ... my @selectedKeys = grep{ $hash{ $_ } eq $scalar } keys %hash; ...

    But doing that, ie. looking up keys in a hash from the values, defeats the purpose of using a hash.

    The chances are that you should really be building the hash the other way around.

    Instead of

    $hash{ $key } = $value;
    you should probably be doing something like
    push @{ $hash{ $value } }, $key;
    .

    Then, when you need the 'keys' associated with a particular 'value' you find them directly:

    ... my @selectKeys = @{ $hash{ $scalar } };

    thus avoiding a potentially costly linear search.

    If you were to describe the bigger picture of your code, and post the code you have so far, rather than just this tiny fragment of requirement, then you'd probably get not just a solution to the small problem, but good advice on solving the bigger problem more effectively.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      So my general problem is one in bioinformatics: given the entire proteome of a species, where each gene can be spliced into multiple protein isoforms of different lengths, pick only the longest protein isoform for each gene

      In other words, if there are genes A, B, C etc.

      with respectively 3, 1 and 2 protein isoform(s)

      a.1, a.2, a.3, b.1, c.2, c.5, c.7, and

      of respective lengths 12, 11, 12, 15, 34, 12, 45, and

      and with their corresponding peptide sequences,

      then, I want the PERL script to return ONLY a.1 or a.3 for gene A and its corresponding sequence, b.1 and its sequence for gene B, c.7 and its sequence for gene C... you get the idea?

      Your suggested syntax using grep worked, except that it returned all the matches into an array, so I used shift to gather the first match (which for my purposes is the same as any other matches, if multiple keys are present as matches to my values in the array)

      Thanks for the useful syntax, I had not come across it yet in the Beginning Perl 3rd edition book, and its only been 10 days since I started teaching myself PERL! So your help is much appreciated...

      I can this my "non-redundification" PRL script that removes protein isoform redundancy by selecting ONLY the longest isoform for each gene in a proteome. My script is ~100 lines long, which I think would be a joke for you Monks! But hey, I am just a Padawan learner as of now! :)

      Thanks again to both of you!

        If all you want is one, then I'd suggest List::Util::first().


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Extract hash keys for values stoted in array
by AnomalousMonk (Archbishop) on May 01, 2011 at 12:53 UTC

    I agree with BrowserUk that we may be able to provide better answers given a better problem definition. This sounds a bit like an XY Problem.

    However, an alternative approach is to 'invert' the hash. This will give very fast lookup of a key for a value in the original hash, but will only ever return one single key, the same one every time, chosen at random, from the set of all keys in the original hash having a particular value. (Note that the 'random choice' referred to here occurs when the original hash is inverted, and is fixed thereafter.)

    >perl -wMstrict -le "my %hash = qw(one 1 uno 1 un 1 two 2 tres 3); my %hsah = reverse %hash; ;; for my $v (1, 2, 4) { print qq{value '$v' }, exists $hsah{$v} ? qq{is $hsah{$v} } : q{does not exist }, q{in original hash}; } " value '1' is uno in original hash value '2' is two in original hash value '4' does not exist in original hash