Erik Olson
Erik Olson

Reputation: 195

Hashes: testing a key

A program reads a very large dictionary-style TXT file into a hash. Sometimes, there is a lower case version of a key that is preferable. My solution below is clumsy because it searches twice even if we already know the lc version exists:

if ( exists $hash{ lc $key } ) {
   $key = lc $key;
}
if ( exists $hash{ $key } ) {
    # lot of code involving $key
}
else {
    # the key doesn't exist, other code
}

Is there a way to avoid two exists tests? If lc $key exists I want to do the identical code to it as in the second if but I need to know which version, lc or not, of $key to use. I'm hoping to condense it to one if-else pair.

Knowing the case of the valid key is important for the rest of the program since it is used to look up information in another hash.

Upvotes: 0

Views: 82

Answers (3)

ikegami
ikegami

Reputation: 386696

if ( my ($real_key) = grep { exists($hash{$_}) } lc($key), $key ) {
   ...
} else {
   ...
}

or

my $real_key =
     exists($hash{ lc($key) }) ? lc($key)
   : exists($hash{ $key     }) ? $key
   : undef;

if (defined($real_key)) {
   ...
} else {
   ...
}

Sure, it still searches twice, but so what? You could use List::Utils's first, but I think replacing a hash lookup with a sub call could actually slow down the code!

Upvotes: 3

Håkon Hægland
Håkon Hægland

Reputation: 40778

You could also do this:

$key = do { my $temp = lc $key; exists $hash{$temp} ? $temp 
      : ( exists $hash{$key} ? $key : undef) };

if ( defined $key ) {
    # lot of code involving $key
}
else {
    # the key doesn't exist, other code
}

Upvotes: 0

Matt Jacob
Matt Jacob

Reputation: 6553

You could use first from List::Util. It will return the first list element where the result from the code block is a true value, or undef if the block never returns true.

use List::Util qw(first);

$key = first { exists($hash{$_}) } lc($key), $key;

if (defined($key)) {
    # ...
}

Upvotes: 0

Related Questions