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

I am trying to find a way to search for matching keys in two hashes without case sensitivity. See the following code for a quick example:
%hash1 = ("TEXT", 25); %hash2 = ("text", 25); foreach $item1 (keys(%hash1)) { foreach $item2 (keys(%hash2)) { if (exists $hash1{$item2}) { print "matches\n";} } }
This doesn't match since one hash key is lower case "text" and the other is upper case "TEXT". What can I do to compare them without case sensitivity so they end up matching?

Thank you.

Replies are listed 'Best First'.
Re: Comparing two hashes for duplicate keys with no case sensitivity
by FunkyMonk (Bishop) on Aug 14, 2008 at 18:15 UTC
    Use uc or lc to force the keys to upper or lower case. Here's an example using uc:

    my %hash1 = ("TEXT", 25); my %hash2 = ("text", 25); foreach my $item2 (keys(%hash2)) { if (exists $hash1{uc $item2}) { print "matches\n";} }

    In real code, I'd probably be changing the keys as I inserted them into the hash (if possible):

    $hash1{lc $item1} = $value1; # etc

    Update:

    PS You don't need the outer loop you had in your original code. You'll notice you don't use $item1 anywhere.


    Unless I state otherwise, all my code runs with strict and warnings
Re: Comparing two hashes for duplicate keys with no case sensitivity
by jhourcle (Prior) on Aug 14, 2008 at 19:46 UTC

    Yet another option:

    Use Hash::Case so that your hashes are case insensitive, and you can test directly.

Re: Comparing two hashes for duplicate keys with no case sensitivity
by toolic (Bishop) on Aug 14, 2008 at 18:14 UTC
    One way is to copy one hash and convert all keys to lower case. This may be costly if the hash is big:
    use strict; use warnings; my %hash1 = ("TEXT", 25); my %hash2 = ("text", 25); my %hash1a; for (keys %hash1) { $hash1a{lc $_} = $hash1{$_} } for (keys %hash2) { if (exists $hash1a{lc $_}) { print "matches\n";} }

    prints:

    matches

    Update: This will work even if the keys are mixed case: teXT. If the keys are all upper case or all lower case, then FunkyMonk's solution is simpler

Re: Comparing two hashes for duplicate keys with no case sensitivity
by jethro (Monsignor) on Aug 14, 2008 at 18:17 UTC

    You already loop through every key of %hash1, so you don't need to check in the hash again

    %hash1 = ("TEXT", 25); %hash2 = ("text", 25); foreach $item1 (keys(%hash1)) { foreach $item2 (keys(%hash2)) { if (lc $item1 eq lc $item2}) { print "matches\n";} } }

    By the way, as long as your hashes are small (say <200 entries) or time is not important, your algorithm is fine, but if not, then you might generate a second hash with the lowercase keys of one of the hashes and then check the second hash against that:

    %hash1 = ("TEXT", 25); %hash2 = ("text", 25); foreach $item1 (keys(%hash1)) { $lchash1{lc $item1}=1; } foreach $item2 (keys(%hash2)) { if (exists $lchash1{lc $item2}) { print "matches\n";} } }
Re: Comparing two hashes for duplicate keys with no case sensitivity
by Skeeve (Parson) on Aug 14, 2008 at 19:14 UTC

    TIMTOWTDI

    my %hash1 = ("TEXT", 25,"test",26,"tset",27); my %hash2 = ("text", 25,"TeSt",24,"xyz",23); foreach (do { my %matcher; @matcher{map lc,keys %hash1}=(); grep exists $matcher{lc $_},keys %hash2; }) { print "$_ matches\n"; }
    This is similar to jethro's second solution but using map and grep instead of loops. And I made his lchash (matcher here) a local variable so that it's gone when the loop is done.

    s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
    +.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e
Re: Comparing two hashes for duplicate keys with no case sensitivity
by GrandFather (Saint) on Aug 14, 2008 at 22:43 UTC

    You don't indicate why you need to do this so the following may be rather more than you need. However, this finds all case insensitive matching keys and reports them:

    use strict; use warnings; my %hash1 = (TEXT => 25, Text => 25, Wibble => 20); my %hash2 = (text => 25, TexT => 25, wibble => 20); my %h1Keys; my %h2Keys; push @{$h1Keys{lc $_}}, $_ for keys %hash1; push @{$h2Keys{lc $_}}, $_ for keys %hash2; my @common = grep {exists $h2Keys{$_}} keys %h1Keys; print "Hash 1 keys \[@{$h1Keys{$_}}\] match \[@{$h2Keys{$_}}\] in hash + 2\n" for @common;

    Prints:

    Hash 1 keys [Wibble] match [wibble] in hash 2 Hash 1 keys [TEXT Text] match [text TexT] in hash 2

    BTW, always use strictures (use strict; use warnings; - see The strictures, according to Seuss).


    Perl reduces RSI - it saves typing
Re: Comparing two hashes for duplicate keys with no case sensitivity
by injunjoel (Priest) on Aug 14, 2008 at 23:25 UTC
    More of TIMTOWTDI,
    #!/usr/bin/perl -w use strict; my %hash1 = (TEXT => 25, Text => 25, Wibble => 20, foo=>42, BAZ => 0); my %hash2 = (text => 25, TexT => 25, wibble => 20, bar=>43, bAz => -1) +; my @dups = do{ local %_; $_{$_}++ for(map lc, (keys %hash1, keys %hash2)); delete @_{map $_{$_} == 1 ? $_ : (), keys %_}; sort keys %_; }; print "@dups \n";
    Have a look at The Uniqueness of hashes. to see similar examples.

    -InjunJoel
    "I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo
Re: Comparing two hashes for duplicate keys with no case sensitivity
by Lawliet (Curate) on Aug 14, 2008 at 18:10 UTC

    Few ideas:

    • Use regular expressions when matching
    • Convert both to lower or upper case, then match
    • Super Search

    I'm so adjective, I verb nouns!

    chomp; # nom nom nom

      In order:

      • Avoid regular expressions when matching. No matter how fast the gurus make the engine, it'll never beat eq for a full, exact match (though it may tie eq). And it'll never catch up with a hash lookup. And it can easily get convoluted (see Regexp::Common). Don't avoid it, of course, when it provides the power you need. But when lc and eq or index provide all the power you need, use them. The regular expression engine provides a lot of power a domain-specific language (DSL). So does a nail gun. Don't forget your other tools when they're more appropriate.
      • Good idea. But you don't really want to go through a second list to find matches from a first list. You don't want a loop in a loop - that becomes O(n^2). Instead, if you can construct the second list as a hash as toolic does (O(n) time), and then loop through the first list (O(n) time) looking each one up in the generated hash with your morphing function (in this case, lc or uc) which is O(1) time, you'll get O(n + n * 1) = O(2n) = O(n) time. Much faster. Now, maybe this is what you're suggesting, but in your race to be the first poster, you missed some helpful details ;-)
      • Super Search for what? Help Super Search Newbies out a bit by giving them a clue as to what to look for. If you don't know, be more gracious about it and admit so ;-) "I'm sure that Super Search would turn something up - did you try that?" It's always better if you've tried it and found things, though, especially since now Super Search will give you a perlmonks-style link ([href://...]) that contains the search terms you've used.
      (That said, welcome to the monastery - you're starting to get the hang of things now.)

        Thanks for the corrections :D
        Few comments:

        "Avoid regular expressions when matching."

        Isn't that the point of them though? Don't we use them to match things?

        "Now, maybe this is what you're suggesting"

        Sure was.

        "but in your race to be the first poster, you missed some helpful details"

        Sure did ;P

        "Help Super Search Newbies out a bit by giving them a clue as to what to look for."

        Will do!

        (Oh, and thanks for the welcome. One and a half months to almost catch on to the norm. Not bad eh?)

        I'm so adjective, I verb nouns!

        chomp; # nom nom nom

Re: Comparing two hashes for duplicate keys with no case sensitivity
by betterworld (Curate) on Aug 14, 2008 at 18:17 UTC
    foreach $item1 (keys(%hash1)) { foreach $item2 (keys(%hash2)) { if (exists $hash1{$item2}) { print "matches\n";} } }

    This code is a bit confusing because you don't use $item1 at all. If you have two nested loops anyway, you don't need exists and can simply compare lc($item1) eq lc($item2).

Re: Comparing two hashes for duplicate keys with no case sensitivity
by pat_mc (Pilgrim) on Aug 14, 2008 at 19:02 UTC
    Hi, Anonymous -

    I understand you want to check case-insensitively which keys in hash2 are also keys of hash1. The following solution might be a good option for large hashes as it only involves one loop:
    my $string = join " ", keys %hash1; foreach my $item ( keys( %hash2 ) ) { print "$item matches" if ( $string =~ /\b$item\b/i ); }

      That's clever, but I count two loops (and you should probably use \Q...\E as well).

        Hm ... where is the second loop you're seeing, chromatic? Is the join operator an undercover loop as well? Clearly, if the complexity of the join operation is equivalent to that of a regular for loop we don't gain much.

        Cheers -

        Pat