Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Ignore Case when comparing Hash Keys

by avidcoder (Novice)
on Nov 15, 2010 at 18:28 UTC ( [id://871532]=perlquestion: print w/replies, xml ) Need Help??

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

I have 2 hashes and I am comparing both the hashes. First I check if the Key from hash2 exists in the hash1 and then compare the values. So the question is how do I ignore case when comparing the Hash keys in the below example code.
#! /usr/bin/perl # %hash1 = ("John", 43, "Paul", 25, "Marie", 22); %hash2 = ("john", 43, "Paul", 25, "marie", 22); while (($KEY_2, $VALUE_2) = each %hash2){ if (exists $hash1{$KEY_2}){ print "$KEY_2 : Matched\n"; } else{ print "$KEY_2 : Did not match\n"; } }
In the above example, John does not match with john as keys have different case. How do I ignore case when comparing the keys in the above example?
Here is the output of the script when you run it john : Did not match Paul : Matched marie : Did not match
Your help is much appreciated.

Replies are listed 'Best First'.
Re: Ignore Case when comparing Hash Keys
by ELISHEVA (Prior) on Nov 15, 2010 at 19:12 UTC

    exists can only do case sensitive comparisons. Instead of doing exists, extract the keys from both hashes using keys, and then lower case them both before comparison:

    Note: this solution preserves the case in both hashes by storing keys in a temporary third hash.

    # make temporary hash using lower case keys only # hash allows for fast lookup of matching key later on in # our for loop my %hashLowercaseKeys1 = ( map { lc($_) => 1 } keys %hash1 ); foreach my $k (keys %hash2) { if (exists $hashLowercaseKeys1{lc($k)}) { print "$k: matched\n"; } else { print "$k: no match\n"; } }
      Thanks a lot. This works.
Re: Ignore Case when comparing Hash Keys
by JavaFan (Canon) on Nov 15, 2010 at 18:55 UTC
    Your description talks about comparing values, but your code doesn't. Also, comparing keys while ignoring case doesn't make the matching unique. What should the output be if
    %hash1 = ("John", 43, "jOHn", 42, "JOHN", 41); %hash2 = ("john", 42);
      Thanks you for your response. You are right. But I am not comparing names in my code but I am comparing the Config attributes where we will always have unique names. For simplicity I just put the names in the example above.
Re: Ignore Case when comparing Hash Keys
by ctilmes (Vicar) on Nov 15, 2010 at 19:15 UTC
    When you transform the keys to lowercase for a case-insensitive lookup, keep the old name around for printing later.
    #! /usr/bin/perl %hash1 = ("John", 43, "Paul", 25, "Marie", 22); %hash2 = ("john", 43, "Paul", 25, "marie", 22); my %lc_hash1 = map { lc $_ => { name => $_, value => $hash1{$_} } } keys %hash1; while (($KEY_2, $VALUE_2) = each %hash2){ if (exists $lc_hash1{lc $KEY_2}){ print "$KEY_2 : Matched\n"; } else{ print "$KEY_2 : Did not match\n"; } }
    Now you can use $lc_hash1{$SOMEKEY}{name} for the original key and $lc_hash1{$SOMEKEY}{value} for the value.
      Or just store the original key as the value of the canonicalized (lower-cased) hash. Look up in the lower case hash to find the key, then use the value of that hash to look up the value in the original hash.
      #! /usr/bin/perl %hash1 = ("John", 43, "Paul", 25, "Marie", 22); %hash2 = ("john", 43, "Paul", 25, "marie", 22); my %lc_hash1 = map { lc $_ => $_ } keys %hash1; while (($KEY_2, $VALUE_2) = each %hash2){ if (exists $lc_hash1{lc $KEY_2}){ print "$KEY_2 : Matched\n"; print "Keys are: $lc_hash1{lc $KEY_2}, $KEY_2\n"; print "Values are: $hash1{$lc_hash1{lc $KEY_2}}, $VALUE_2\n"; } else{ print "$KEY_2 : Did not match\n"; } }
        ctilmes --- Thanks a lot. This works great!!! Just the way I want it to. You saved me a ton of time. Thankyou all.
Re: Ignore Case when comparing Hash Keys
by moritz (Cardinal) on Nov 15, 2010 at 18:40 UTC
    First transform one of the hashes to use all lowercase keys. Then check existence of lc $other_key.

    Here's a Perl 6 solution:

    my %lc1 = %hash1.keys>>.lc Z %hash1.values; for %hash2.keys -> $k { say "$k ", %lc1.exists($k) ?? 'Matched' !! 'Did not match'; }

    Transforming the code to Perl 5 shouldn't be too hard.

      I cant convert the Hash Keys to lower case, because I want to display the Keys and Values Side by side in the output and I want to keep the original Case of the Keys. I was wondering if there is something like if ($a =~ /$search/i) kind of comparison that ignores the case and matches the keys. Also I want the hash to give me the value of the key

        avoidcoder:

        I don't advise it, but you could do something like:

        %hash1 = ("John", 43, "Paul", 25, "Marie", 22); %hash2 = ("john", 43, "Paul", 25, "marie", 22); while (($KEY_2, $VALUE_2) = each %hash2){ if (grep { m/$KEY_2/i } keys %hash1) { print "$KEY_2 : Matched\n"; } else{ print "$KEY_2 : Did not match\n"; } }

        Note: Untested, you can keep all the bits when it breaks, etc.

        ...roboticus

        I cant convert the Hash Keys to lower case, because I want to display the Keys and Values Side by side in the output and I want to keep the original Case of the Keys.

        Then use another hash that maps lower case key to the correctly cased key.

        Also note that the original hash with the correctly cased keys still exists, and stands at your disposal.

Re: Ignore Case when comparing Hash Keys
by toolic (Bishop) on Nov 15, 2010 at 19:14 UTC
Re: Ignore Case when comparing Hash Keys
by raybies (Chaplain) on Nov 15, 2010 at 19:27 UTC
    I'd create a translate key hash (called %xl8 in code example below).
    %hash1 = ("John", 43, "Paul", 25, "Marie", 22); %hash2 = ("john", 43, "Paul", 25, "marie", 22); foreach (keys %hash1) { $xl8{lc $_} = $_; } while (($KEY_2, $VALUE_2) = each %hash2){ if (exists $xl8{lc $KEY_2}){ print "$KEY_2 : Matched\n"; } else{ print "$KEY_2 : Did not match\n"; } }
    (looks like I was too slow... someone else suggested same with the elegant "map" solution. :)

      Not only was ELISHIVA quicker to the draw, but she also gave a strictures compliant example and used nicer variable names. Example code should demonstrate best practice so far as reasonable.

      True laziness is hard work
Re: Ignore Case when comparing Hash Keys
by aquarium (Curate) on Nov 15, 2010 at 21:53 UTC
    i think you want to avoid anomalies, such as one of the hashes having multiple same values once upper/lower-cased, e.g. McGirk and Mcgirk...which would break under current arrangements. might be an idea to have the hashes keyed by lowercase/uppercase, but also store original print/visual/mixed-case value as part of the hashes.
    the hardest line to type correctly is: stty erase ^H

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://871532]
Approved by ELISHEVA
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2024-03-28 21:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found