Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Common hash keys

by Anonymous Monk
on Jun 07, 2008 at 07:16 UTC ( #690802=perlquestion: print w/replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Given two hash references, how can I determine if they have any keys in common?

Replies are listed 'Best First'.
Re: Common hash keys
by moritz (Cardinal) on Jun 07, 2008 at 07:36 UTC
    This is described (in a slight variation) in perlfaq4. If you have problems with having references and not the hash itself, read perlreftut.

      Sorry, but none of these

      Data: Hashes (Associative Arrays) How do I process an entire hash? What happens if I add or remove keys from a hash while iterating ove +r it? How do I look up a hash element by value? How can I know how many entries are in a hash? How do I sort a hash (optionally by value instead of key)? How can I always keep my hash sorted? What's the difference between "delete" and "undef" with hashes? Why don't my tied hashes make the defined/exists distinction? How do I reset an each() operation part-way through? How can I get the unique keys from two hashes? How can I store a multidimensional array in a DBM file? How can I make my hash remember the order I put elements i into it? Why does passing a subroutine an undefined element in a hash create +it? How can I make the Perl equivalent of a C structure/C++ class/hash +or array of hashes or arrays? How can I use a reference as a hash key?

      relate to my question. Which for clarity I will restate. How can I determine if there are any common keys in two hashes to which I have references.

      Specifically, the code

      if(@{$refa}{keys %{$refb}}) { # }

      does not work.

      If you do not know the answer, perhaps you could let someone who does respond? Instead of linking to a generic section of Q&As that you think (hope) might answer it.

      If you really believe one of those does answer the question, then perhaps you could link directly to it, rather than just waving your hands in a general direction: "it's over there somewhere".

        Sorry, but none of these
        Data: Hashes (Associative Arrays) <<<snip>>> How can I get the unique keys from two hashes? <<<snip>>>
        relate to my question.

        You suffer from a failure of the imagination. The preceding item does relate to your question. It's the complementary action to the one you requested. Compute the list of unique keys and all your keys not on that list are the ones common to your two hashes. Alternatively, modify the algorithm to keep the keys that don't satisfy the uniqueness criterion and you have the list of keys you want.

        If that's not obvious, you might want to reconsider your career choices...or at least refrain from posting snotty replies to any assistance you are given.

        Sorry, but none of these (...) relate to my question.

        Well, they are not the only topics in perlfaq4. I was referring to intersection of arrays. keys %hash is a list after all, and although the FAQ speaks of arrays, it works for lists as well.

Re: Common hash keys
by apl (Monsignor) on Jun 07, 2008 at 12:33 UTC
    Ignoring CPAN modules, If you can't think of at least two purely mechanical ways of solving this problem, you need to either read (among other things) Data Type: Hash, or drop the class you're in.
Re: Common hash keys
by throop (Chaplain) on Jun 07, 2008 at 13:23 UTC
    use strict; use vars qw($hashRef1 $hashRef2); #code that puts values into the hashes my @keyIntersection = grep exists($hashRef2->{$_}), keys %$hashRef1; # Or if you just want to know if there are any use List::Util; my $hasInt = first {exists $hashRef2->{$_}} keys %$hashRef1;
    $hasInt will be undef unless there's an intersection.
    But be careful, it will be 0 if the first interecting key is '0'


      There's no need to be careful if you handle the 0 case directly:

      use List::Util; my $hasInt = defined first { exists $hashRef2->{$_} } keys %$hashRef1 ;


        Actually, there's no need to be careful or test for definedness.

        List::Util::first() returns the result of the code block, in this case the boolean return of the exists function. The values associated with the keys never come into it.

        use List::Util qw[ first ];; my $a = { a=>0, b=>0, c=>0 }; my $b = { c=>0, d=>0, e=>0 }; if( first{ exists $b->{ $_ } } keys %$a ) { print "Common keys!"; } Common keys!

        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.

      *That's the one*. Thank you.

      use List::Util 'first'; if(first{exists $hashRef2->{$_}} keys %$hashRef1 ) # }
Re: Common hash keys
by ferreira (Chaplain) on Jun 07, 2008 at 14:45 UTC

    It should be as simple as that:

    my @common_keys = grep { exists $hash1->{$_} } keys %$hash2;
Re: Common hash keys
by casiano (Pilgrim) on Jun 07, 2008 at 10:01 UTC
    If you want to know which keys they have in common:
    DB<1> %a = (a => 1, b =>2, c => 3); %b = (b =>2, c => 1, d => 4) DB<2> $seen{$_}++ for (keys(%a), keys(%b)) DB<3> x grep { $seen{$_} > 1 } keys(%seen) 0 'c' 1 'b'
    If the only thing you want is to know if they have some key in common:
    DB<1> use List::Util qw{first} DB<2> %a = (a => 1, b =>2, c => 3); %b = (b =>2, c => 1, d => 4) DB<3> x first { ++$seen{$_} > 1 } (keys(%a), keys(%b)) 0 'c'
    Hope it helps

      DB<2> $seen{$_}++ for (keys(%a), keys(%b)) DB<3> x grep { $seen{$_} > 1 } keys(%seen)
      You can combine these two into a single statement:
      use Data::Dump; my %a = ( a => 1, b => 2, c => 3 ); my %b = ( b => 2, c => 1, d => 4 ); my %seen; pp grep { $seen{$_}++ } keys %a, keys %b;

      Update: Don't use state variables for this. See lodin's reply

      Or, with 5.10

      pp grep { state %seen; $seen{$_}++ } keys %a, keys %b;

      Unless I state otherwise, all my code runs with strict and warnings

        Be careful with that state. It won't do what (I think) you want.

        use 5.010; sub foo { my ($a, $b) = @_; sort grep { state %seen; $seen{$_}++ } keys %$a, keys %$b } my %a = ( a => 1, b => 2, c => 3 ); my %b = ( b => 2, c => 1, d => 4 ); say join ' ', foo(\%a, \%b); say join ' ', foo(\%a, \%b); __END__ b c a b b c c d
        Here you should just stick with my.


Re: Common hash keys
by parv (Priest) on Jun 07, 2008 at 07:30 UTC

    What have you tried? Pseudocode would be ...

    for all keys in hash-1, where k-1 is current key for all keys in hash-2, where k-2 is current key put k-1 in store if k-1 = k-2 end-for end-for # "store" would have all the common keys in the end

    Time passes. Oh yes, finding if any keys are present ...

    common set to false for all keys in hash-1, where k-1 is current key for all keys in hash-2, where k-2 is current key if k-1 = k-2, common set to true break out of both loops end-for end-for # test common to check for commonality
      Your inner loop could be described as clubbing somebody to death with a loaded uzi. No need to iterate over the hash keys just to check if one is present. Looking up a key in a hash is the most basic operation a hash offers ;-)

        (: Indeed! (But I did not write Perl, did I?)

Re: Common hash keys
by radiantmatrix (Parson) on Jun 09, 2008 at 20:46 UTC

    OK, let's walk through this.

    A hash reference is just a reference to a hash, getting at the hash is a simple matter -- so we can ignore the "reference" part of your question for now, and focus on how to determine if two hashes share any keys. Let's start with two simple hashes:

    %a = ( one=>1, two=>2, three=> 3 ); %b = ( two=>'two', three=>'three', four=>'four');

    We know the keys they have in common are two and three, but how to find that out in code?

    First, let's rephrase the question a little bit: if I know all the keys in one hash, how do I find out if any of those keys exist in another hash?

    Let's work backwards from that. How do we find out if the %a hash contains a key -- let's say, the key one? That would be exists:

    if (exists $a{one}) { print "\%a has 'one' for a key" }

    Ok, so how do we find out what keys a hash has? How about keys:

    print join(",", keys %a); # prints "one,two,three"

    So, if we get the list of keys for hash %a, all we have to do is loop through them and see if %b has any that match. Here's the long way:

    my @common_keys; foreach my $key (keys %a) { if (exists $b{$key}) { print "\%a and \%b have key '$key' in common\n"; push @common_keys, $key; } }

    Make sense? Of course, this type of activity -- looking through a list to find items that meet a criterion -- is so common that Perl has a function for it: grep. So here's a short version:

    my @common_keys = grep { exists $b{$_} } keys(%a);

    Now let's add "references" back to the mix. Let's make references to our hashes:

    $x = \%a; $y = \%b;

    Now, we do as above, but dereferencing as appropriate:

    my @common_keys = grep { exists $y->{$_} } keys( %{ $x } );

    We pass the whole hash referenced by $x to keys; and we use the dereference operator -> to make sure that exists looks at the hash referenced by $y.

    Ramblings and references
    “A positive attitude may not solve all your problems, but it will annoy enough people to make it worth the effort.” Herm Albright
    I haven't found a problem yet that can't be solved by a well-placed trebuchet
Re: Common hash keys
by injunjoel (Priest) on Jun 08, 2008 at 20:46 UTC
    For the sake of esoteric aesthetics...
    #!/usr/bin/perl -w use strict; my %a = ('a' =>1, 'b'=>2, 'c'=>5, 'd'=>19); my %b = ('b'=>12, 'd'=>32, 'z'=>77, 'y'=>5); my @common_keys = do{ local %_; $_{$_}++ for(keys %a, keys %b); delete @_{(map{ ($_{$_} == 1) ? $_ : ()}keys %_)}; sort keys %_; }; print "@common_keys";
    "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: Common hash keys
by wade (Pilgrim) on Jun 09, 2008 at 16:06 UTC

    Is it possible, given that the OP was so terse and so aggressive (in later posts, though I admit that I'm making assumptions), that this is a troll?

      And look at who was aggressively defending the aggressive troll...

        Ooooh Mummy. He's soo harsh and aggressive. He said I didn't understand hashes and that he'd wait for a better answer.

        Mummy. Make the bad man go away.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://690802]
Approved by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2017-03-30 01:49 GMT
Find Nodes?
    Voting Booth?
    Should Pluto Get Its Planethood Back?

    Results (353 votes). Check out past polls.