Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Comparing hashes without sorting the keys

by Discipulus (Curate)
on Feb 18, 2004 at 18:59 UTC ( #330008=perlquestion: print w/ replies, xml ) Need Help??
Discipulus has asked for the wisdom of the Perl Monks concerning the following question:

hello monks!
I was inteded to compare two hash and I fall in this:
%aa=(uno=>1, due=>2); %bb=(due=>2, uno=>1); if (%aa eq %bb){print "equal\n"} else {print "boh?\n"} __END__ :>equal ########################### %aa=(uno=>1, due=>2); %bb=(due=>2, uno=>1111111322321111111111); if (%aa eq %bb){print "equal\n"} else {print "boh?\n"} __END__ :>equal

Why ? there is some smart way to compare two hash without foreach sort keys ??
%aa eq %bb seems to compare only the keys..

TIMT1WToLooseReputation
greetings from sunny roma
lor*

20040220 Edit by BazB: Changed title from '%a eq %b'

Comment on Comparing hashes without sorting the keys
Download Code
Re: Comparing hashes without sorting the keys
by borisz (Canon) on Feb 18, 2004 at 19:02 UTC
Re: Comparing hashes without sorting the keys
by diotalevi (Canon) on Feb 18, 2004 at 19:05 UTC

    That doesn't compare the keys. It compares the number of hash buckets vs hash buckets used values for each hash. You are comparing strings like "1/16" and "24/32" which is only of use when you are debugging hash bucket usage. You can use Storable to serialize your hashes for comparison: Storable::freeze( \%a ) eq Storable::freeze( \%b ).

      thanks Diotalevi,
      can you explain a little more this number of hash buckets vs hash buckets used values for each hash..

      so there is not a smart way to do this ?

      lor*
        The smart way uses either Storable or the (unknown to me) Data::Compare module someone else recommended. You can find more information about hash buckets by using Super Search.
Re: Comparing hashes without sorting the keys
by ysth (Canon) on Feb 18, 2004 at 19:11 UTC
    eq applies scalar context to its operands. scalar(%hash) has a special meaning, search for "hash in scalar context" in perldata. If you want to compare the contents, you'll need to do something more complicated.

    Data::Dumper, Storable, Test::More, and Test::Differences all do things that are close to what you want, but none seems a perfect fit (and you don't say if you just want to test the keys and values for equality or if you want to descend into the values themselves if they are complex structures.)

    Boris's suggestion of Data::Compare seems to be what you want (assuming you want a deep compare or just have a simple hash). It's Compare function returns backward for my taste, though.

      You want it to return false when they match? Errm, why? Of course, if you really want to turn that around, I'd be happy to accept a patch, including documentation and tests, provided that it maintains backwards combatibility - so it has to be an option that the user explicitly turns on.
        Just following the lead of things like cmp. Better yet, name the function to describe what it returns, e.g. "Differ" or "Match" (so
        if (Match($a,$b) or Differ($c,$d))
        does what it says.

        You're the maintainer of Data::Compare? Interesting. Id very much like to chat with you about this module, specifically with regard to Data::Stream. I say this becuase I have considerable experience with this problem and actually wrote a considerable amount of unreleased code on the subject. I note that Data::Compare fails what I consider to be one of the nastiest test cases that exist in perl data structures. (Another test case is what I call the "dogpound" test originally posted by merlyn which afaik, only Data::Stream handles properly.) Don't feel bad, the only modules that don't fail this test are currently the (unreleased to cpan) Data::Stream and (the released to cpan) Data::BFDump (dont bother with the later, Data::Stream was specifically designed to replace Data::BFDump). Anyway, the test case is as follows:

        use Data::Compare; # this test is called "Scalar Cross" in both the Data::BFDump and Data +::Stream test sets my ($a,$x,$y)=([]); $x=\$y; $y=\$x; $a->[0]=\$a->[1]; $a->[1]=\$a->[0]; print Compare([$x,$y],$a); # infinte loop

        My email is on my homenode, id love to correspond with you about this subject if you're interested.


        cheers
        ---
        demerphq

          First they ignore you, then they laugh at you, then they fight you, then you win.
          -- Gandhi


Re: Comparing hashes without sorting the keys
by davido (Archbishop) on Feb 18, 2004 at 19:33 UTC
    Here's a Data::Compare example:

    use strict; use warnings; use Data::Compare; my %aa = qw( uno 1 due 2 ); my %bb = qw( due 2 uno 11234325161111 ); print Compare( \%aa, \%bb ) ? "Equal!\n" : "Unequal!\n";


    Dave

Re: Comparing hashes without sorting the keys
by jonadab (Parson) on Feb 18, 2004 at 20:22 UTC

    eq in Perl is not like eq in lisp, nor is it like equal. It's more like (Perl's) ==, except that it compares strings instead of numbers. What you're doing is evaluating the two hashes in string context and comparing the results. Hashes in string context (or any scalar context) return a piece of technical information you don't care about, so this isn't what you want to do.

    Perl5 doesn't have a built-in operator for what you do want, so you can either write your own hash comparison routine or (preferably) look into using the module that the other monks have suggested. (I believe Perl6 will be fixing this, but you don't want to wait for that, because it'll be a while.)


    ;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
Re: Comparing hashes without sorting the keys
by Popcorn Dave (Abbot) on Feb 18, 2004 at 20:38 UTC
    This seems a bit of work to do strictly in Perl, but this should do what you want to do. However I do believe that davido's suggestion of the module is cleaner to work with as you're not doing the conversion of the hash to a sorted array, then to a string.

    #!/usr/bin/perl/ use strict; my %aa=(uno=>1, due=>2); my %bb=(due=>2, uno=>1); my ($aa,$bb,@aa,@bb); **updated** @aa=sort(%aa); @bb=sort(%bb); $aa = join("",@aa); $bb = join("",@bb); **updated if ($aa eq $bb){ print "Matches!\n"; } else{ print "No match!\n"; }

    Update: As both samtregar and <a href="http://www.perlmonks.org/index.pl?node_id=56270>greenFox point out the original code blows up with the data they show in their nodes. However replacing the

    @aa=%aa; @bb=%bb; $aa = join("",sort @aa); $bb = join("", sort @bb);

    with

    @aa=sort(%aa); @bb=sort(%bb); $aa = join(" ",@aa); $bb = join(" ",@bb);

    I believe should do what I originally intended.

    Sorting the hash, not the array, and leaving the space in should solve the problem found by the other monks.

    There is no emoticon for what I'm feeling now.

      That won't work! Try it on these hashes, which your code considers equal, for example:

      my %aa=(aa=>'aa', bb=>'bb'); my %bb=(a=>'aaa', b=>'bbb');

      -sam

        ++nicely spotted. Even just "flattening" the hash to an array is suicidal, consider
        my %aa=(aa=>'bb', cc=>'dd'); my %bb=(bb=>'aa', dd=>'cc');
        ...ouch!

        --
        Do not seek to follow in the footsteps of the wise. Seek what they sought. -Basho

        You're right! Given the OP's data I just hacked out a quick solution, but thanks for pointing out my flaw.

        Good call.

        There is no emoticon for what I'm feeling now.

      Your new code won't work either! Try it with:

      my %aa=(aa=>'a aa'); my %bb=(a=>'aa aa');

      -sam

        Okay, I give. :)

        svsingh pointed out the same thing to me. Like I said originally, the best way to do it is with the module and the hash reference.

        There is no emoticon for what I'm feeling now.

Re: Comparing hashes without sorting the keys
by Roger (Parson) on Feb 19, 2004 at 14:05 UTC
    A bit of fun before bed time. :-)
    #!/usr/local/bin/perl -w use strict; my %aa=(uno=>1, due=>3); my %bb=(due=>2, uno=>1); my @k_aa = sort keys %aa; my @k_bb = sort keys %bb; my $equal = 0; if ($#k_aa == $#k_bb) { $equal++; for (0..$#k_aa) { $equal--, last if $k_aa[$_] ne $k_bb[$_] } if ($equal) { for (@k_aa) { $equal--, last if $aa{$_} ne $bb{$_} } } } print "\%aa and \%bb are ", @{['not equal', 'equal']}[$equal], "\n";

Re: Comparing hashes without sorting the keys
by falsa_utopia (Initiate) on Feb 20, 2004 at 02:15 UTC
    Also for fun (and in my debut as a novice!):
    #!/usr/bin/perl -w use strict; my %aa=(uno=>1, due=>2, tres => 3); my %bb=(due=>2, uno=>1, tres => 3); print comphash(\%aa, \%bb) ? "Hashes are equal\n" : "Hashes are differ +ent\n"; sub comphash { my ($aa, $bb) = @_; return 0 unless keys %$aa == keys %$bb; my %whole_hash = (%$aa, %$bb); my $different; foreach (keys %whole_hash) { next if exists $aa{$_} && exists $bb{$_} && $aa{$_} == $bb{$_} +; $different++ && last; } return not $different; }
    Cheers!

      There are a few problems with that. If the hash values are not numeric your == will cause trouble. Switching that to eq will get you some way, but then values of '079' and '79' don't compare equal although they may be considered so for the applications use of them.

      Then you get into more esoteric problems. What if the values of the hash are themselves hashes or arrays (references). Comparing the stringyfied references will say that two arrays are different even if their contents is similar as they will have different addresses. Are two content-similar, but physically different arrays considered equal? That's almost a philisophical debate and very much depends on the application.

      That said. Many of these problem exists with most of the other solutions offered also.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      Timing (and a little luck) are everything!
Re: Comparing hashes without sorting the keys
by demerphq (Chancellor) on Feb 20, 2004 at 15:56 UTC
    use strict; use warnings; use Data::Dumper; sub are_hashes_equal { my ($hash1,$hash2)=@_; return 1 if $hash1==$hash2; my %temp=%$hash2; foreach my $key (keys %$hash1) { return 0 unless exists $temp{$key} and defined($hash1->{$key}) eq defined($temp{$key}) and (defined $temp{$key} ? $temp{$key} eq $hash1->{$ke +y} : 1); delete $temp{$key}; } return !keys(%temp); } my %aa=(uno=>1, due=>2,trio=>undef,quat=>0,cinq=>''); my @b=( \%aa, {uno=>1, due=>2,trio=>undef,quat=>0,cinq=>''}, {uno=>1, due=>2,trio=>0,quat=>undef,cinq=>''}, {uno=>2, due=>2,trio=>undef,quat=>0,cinq=>''}, {uno=>2, due=>2,trio=>undef,quat=>0}, {uno=>2, due=>2,trio=>undef,quat=>0,cinq=>'',sex=>'is fun'}, ); print Data::Dumper->Dump([\%aa],['*aa']); foreach my $test_id (0..$#b) { print Data::Dumper->Dump([$b[$test_id]],['*test'.$test_id]); print "Is ",(are_hashes_equal(\%aa,$b[$test_id]) ? '' : 'not '), "the same as %aa\n\n"; }

    produces

    %aa = ( 'cinq' => '', 'trio' => undef, 'due' => 2, 'uno' => 1, 'quat' => 0 ); %test0 = ( 'cinq' => '', 'trio' => undef, 'due' => 2, 'uno' => 1, 'quat' => 0 ); Is the same as %aa %test1 = ( 'cinq' => '', 'trio' => undef, 'due' => 2, 'uno' => 1, 'quat' => 0 ); Is the same as %aa %test2 = ( 'cinq' => '', 'trio' => 0, 'due' => 2, 'uno' => 1, 'quat' => undef ); Is not the same as %aa %test3 = ( 'cinq' => '', 'trio' => undef, 'due' => 2, 'uno' => 2, 'quat' => 0 ); Is not the same as %aa %test4 = ( 'trio' => undef, 'due' => 2, 'uno' => 2, 'quat' => 0 ); Is not the same as %aa %test5 = ( 'cinq' => '', 'trio' => undef, 'due' => 2, 'uno' => 2, 'sex' => 'is fun', 'quat' => 0 ); Is not the same as %aa

    HTH


    ---
    demerphq

      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi


Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://330008]
Approved by b10m
Front-paged by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (12)
As of 2014-09-17 10:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (71 votes), past polls