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

The other day I ran across a scenario where I needed to be able to look up data in a hash by both keys and values. One of the databases we use stores a county as a number field. Some of the legacy software we have that uses the database stores a char array to translate for use in the program. I was writing a web interface to do a search, and needed a way to search the county field by name as opposed to number. After a little dig through the docs, a few suggestions from some people here, and some skull scratching I determined the best course of action in my case since all my values were unique, was to store both names and numbers as keys with the appropriate values like below.

my($count, %il_counties ); my @il_counties = ("blank","ADAMS","ALEXANDER","BOND","BOONE","BRO +WN","BUREAU","COOK","CALHOUN", "CARROLL","CASS","CHAMPAIGN","CHRISTIAN","CLARK","CLAY","CLINT +ON","COLES", "CRAWFORD","CUMBERLAND","DE KALB","DE WITT","DOUGLAS","DUPAGE" +,"EDGAR", "EDWARDS","EFFINGHAM","FAYETTE","FORD","FRANKLIN","FULTON","GA +LLATIN", "GREENE","GRUNDY","HAMILTON","HANCOCK","HARDIN","HENDERSON","H +ENRY","IROQUOIS", "JACKSON","JASPER","JEFFERSON","JERSEY","JO DAVIESS","JOHNSON" +,"KANE","KANKAKEE", "KENDALL","KNOX","LAKE","LA SALLE","LAWRENCE","LEE","LIVINGSTO +N","LOGAN", "MACON","MACOUPIN","MADISON","MARION","MARSHALL","MASON","MASS +AC","MCDONOUGH", "MCHENRY","MCLEAN","MENARD","MERCER","MONROE","MONTGOMERY","MO +RGAN","MOULTRIE", "OGLE","PEORIA","PERRY","PIATT","PIKE","POPE","PULASKI","PUTNA +M","RANDOLPH", "RICHLAND","ROCK ISLAND","ST. CLAIR","SALINE","SANGAMON","SCHU +YLER","SCOTT", "SHELBY","STARK","STEPHENSON","TAZEWELL","UNION","VERMILION"," +WABASH","WARREN", "WASHINGTON","WAYNE","WHITE","WHITESIDE","WILL","WILLIAMSON"," +WINNEBAGO", "WOODFORD" ); $il_counties{$_} = $count++ foreach (@il_counties); $il_counties{$il_counties{$_}} = $_ foreach (keys %il_counties);

By doing this I was able to just plug in either the number of the county or the name, and get back the corresponding data, but again, all my values were unique, so this wouldn't work with non-unique values.

I'm wondering if anyone else has run across the need to retrieve the hash key from a known value. How did you do it? Or if you haven't, how would you do it?

ryddler

Replies are listed 'Best First'.
Re: Finding a hash key from the value (reverse lookup)
by davorg (Chancellor) on Jan 22, 2001 at 19:03 UTC

    The reverse method only works if your values are unique. If they are and you will be doing a lot of reverse lookups then it's worth building a reverse lookup hash. This is far simpler than it sounds:

    my %rev_hash = reverse %hash;
    --
    <http://www.dave.org.uk>

    "Perl makes the fun jobs fun
    and the boring jobs bearable" - me

Re: Finding a hash key from the value (reverse lookup)
by kilinrax (Deacon) on Jan 22, 2001 at 18:36 UTC
    You could always use 'reverse':
    #!/usr/bin/perl -w use strict; my %fruit = ( apple => 'green', orange => 'orange', banana => 'yellow' ); print ${{reverse %fruit}}{'yellow'};
      You couldn't always use reverse.

      The question asked about non-unique values, thus you end up losing keys that are appropriate values. In this situation, you do not have a 1-1 function so reverse is very bad.

      ALL HAIL BRAK!!!

        Well, building a useful reverse index in cases where you don't have a 1:1 mapping isn't too bad (although not as elegant):

        (given %index)

        my %revindex; for my $revpair (map { [ $index{$_}, $_ ] } keys(%index)) { push @{$revindex{$revpair->[0]}}, $revpair->[1]; }

        There are probably simpler ways to do even that, however.

Re: Finding a hash key from the value (reverse lookup)
by PsychoSpunk (Hermit) on Jan 22, 2001 at 23:15 UTC
    My personal solution would be to reverse the hash so the original values are keys, but the resultant values for each key would be an array reference so that the lookup would return all appropriate original keys.

    Update: Some code

    my %original_hash = ( first => 'alpha', second => 'beta', prime => 'alpha', ); foreach $key (keys %original_hash) { push @{$reverse_hash{$original_hash{$key}}}, $key; } foreach $rkey (keys %reverse_hash) { print "$rkey = ".(join ', ', @{$reverse_hash{$rkey}})."\n"; } print "\n"; my %test_hash = reverse %original_hash; foreach $tkey (keys %test_hash) { print "$tkey = $test_hash{$tkey}\n"; }

    Just a little demonstration loop at the end.

    Update' I managed to reproduce the solution in the Cookbook without checking it first. First off, I'm happy about doing such. Second, I figured I'd go ahead and attribute it as such a solution, even though I didn't look ahead of time.

    ALL HAIL BRAK!!!

Re: Finding a hash key from the value (reverse lookup)
by Beatnik (Parson) on Jan 22, 2001 at 18:49 UTC
    If you happen to have the Perl Cookbook, Chapter 5 describes the reverse lookup method (as mentioned above) on page 142.

    Greetz
    Beatnik
    ... Quidquid perl dictum sit, altum viditur.