Pathologically Eclectic Rubbish Lister PerlMonks

### Need a test for deep equality

by rinceWind (Monsignor)
 on Apr 20, 2002 at 10:48 UTC Need Help??
rinceWind has asked for the wisdom of the Perl Monks concerning the following question:

Hello brethren,

I'm writing some test for a new module, and I need a way of telling whether two arbitrarily complex data structures are identical in content.

One of the tests involves persisting a structure and reading it back - expecting the results to be identical. I tried Data::Dumper, and this almost worked - alas, the order of key/value pairs inside hashes is not preserved, hence the strings are not identical.

One thought would be to use Storable, but Storable with knobs on (or in this case hooks :-) is what I am in fact testing.

I either want an equality test that copes properly with hashes: {a=>'foo',b=>'bar'} equals {b=>'bar',a=>'foo'}, or a safe, guaranteed serialisation module that presents hash keys sorted.

I'm quite capable of writing a deep equality routine, but just a tad reluctant to re-invent the wheel.

Apologies if this information is already in the monks database, but Super Search aint what it used to be :-(.

--rW

Replies are listed 'Best First'.
Re: Need a test for deep equality
by Corion (Pope) on Apr 20, 2002 at 10:59 UTC

While implementing my module File::Modified (nee File::Dependencies - not yet on CPAN), I also wanted such a deep comparision method, but I didn't find one either (in Python, this would be as easy as struct_a == struct_b :-)). But I would prefer basing the thing on Data::Dumper or another existing wheel, as cyclic structures are a nasty thing to handle. On the problem of hash order, it would make sense to do a sort() on the keys of the hashes before writing them out, so that identical hashes produce identical strings - maybe that would be just a small patch to Data::Dumper.

Update : Of course, somebody already invented this wheel, in Test::More by Michael Schwern. There is the routine is_deeply, which does a deep compare - maybe this would warrant extraction in its own module...

perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ;    # The
$d = new HTTP::Daemon and fork and getprint$d->url and exit;#spider
($c =$d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web [download] maybe that would be just a small patch to Data::Dumper. Perhaps, but somehow I doubt it'll happen. First off you would have to do it twice, once in C and once in perl. Second off Data::Dumper is optimized for speed and the overhead of sorting, especially a tied structure, could get prohibitive. OTOH Data::BFDump does sort its keys, and IIRC so does Data::Dump. Yves / DeMerphq --- Writing a good benchmark isnt as easy as it might look. Re: Need a test for deep equality by andreychek (Parson) on Apr 20, 2002 at 15:43 UTC There is a module for this over at CPAN called Struct::Compare. From the description: Compares two values of any type and structure and returns true if they are the same. It does a deep comparison of the structures, so a hash of a hash of a whatever will be compared correctly. So, you just pass in two structures, and it returns true or false, based on whether or not the structures are equal. Hope that helps! -Eric Re: Need a test for deep equality by gav^ (Curate) on Apr 20, 2002 at 21:52 UTC You might want to look at Data::Compare: use Data::Compare; my$hr1 = {a=>'foo',b=>'bar'};
my $hr2 = {b=>'bar',a=>'foo'}; print "They are ", (Compare($hr1,$hr2) ? "the same" : "different"), "\ +n"; [download] gav^ Re: Need a test for deep equality by Dog and Pony (Priest) on Apr 20, 2002 at 11:08 UTC Maybe this isn't possible, but you could use something like Tie::IxHash to have the hashes always return the same order of keys. Otherwise, perlfaq4 has examples on how to do this using FreezeThaw. Maybe one of these might be what you need. You have moved into a dark place. It is pitch black. You are likely to be eaten by a grue. Re: Need a test for deep equality by rinceWind (Monsignor) on Apr 21, 2002 at 09:49 UTC Update: What I didn't specify, but perhaps should have, is that parts of the data structure are blessed object references, and I want to compare the guts of the objects. ### Conclusion: ModuleConclusion Data::DumperHashes not consistently serialised Test::MoreOnly does a shallow comparison. Tie::IxHashSolves a different problem. Anonymous hashes cannot be tied FreezeThawOnly copes with shallow structures Struct::CompareDoes not handle references Recursive comparison by thraxilDoes not handle object references Data::CompareDoes not handle references Conclusion: either fix Data::Dumper or roll my own compare routine :-<. Update 2: I have been looking further at Test::More. There are numerous versions floating about, owing to the way in which people have used it to package up their tests in their own modules. The definitive Test::More on CPAN has a function eq_hash, which suffers from the same syndrome as Data::Dumper when it comes to ordering of keys. Update 3: simonm++ The module Class::MakeMethods::Utility::Ref does the business! Shame about the name - something as useful should not IMO have a module name 4 levels down, with no clue as to what it can do. I have to wonder if you could canoncalize the hashes. A simple "alphabetize the keys at each level" comes to mind. This could be expensive for large/deep hashes, but, it may be worth it to you. Re: Need a test for deep equality by thraxil (Prior) on Apr 20, 2002 at 17:55 UTC Re: Need a test for deep equality by ides (Deacon) on Apr 21, 2002 at 22:26 UTC Maybe I am missing something here but is there a reason you just don't compare the hashes by hand? Using recursion if they are hashes of hashes,etc? Something along these lines?  my %hash1 = { 'foo' => 1, 'bar' => 1 }; my %hash2 = { 'foo' => 1, 'bar' => 1 }; foreach my$key ( keys(%hash1) ) {
die "Not equal" if $hash1{$key} != $hash2{$key};
}
[download]
I think you may be making this harder than it needs to be, either that or I'm not understanding your problem fully.

-----------------------------------
Frank Wiles <frank@wiles.org>
http://frank.wiles.org

Re: Need a test for deep equality
by willijar (Initiate) on Apr 22, 2002 at 12:03 UTC
While there are many Perl functions that could do this I would recommend rolling your own specific to your application. It isn't really reinventing the wheel as every application and structure will probably require a different definition for equality. For example are two structures equal only if stored at the same location or if they contain the same data. WHat about when comparing the object contents. At what depth do yuo compare contents rather than identity. Similarly for vectors. In comparing Strings should they be case insensitive. etc. Can an integer be equal to a float with the same printed representation. It isn't a simple question with simple answers.
Re: Need a test for deep equality
by simonm (Vicar) on Apr 23, 2002 at 05:00 UTC
Yes, I've got a module on CPAN that does this -- see the ref_compare function in Class::MakeMethods::Utility::Ref, based on some old code by David Muir Sharnoff. It should handle scalar values, plain hashes or arrays, or arbitrary nested data structures.

-Simon

Re: Need a test for deep equality
by KILNA (Acolyte) on May 02, 2002 at 21:21 UTC
Another option is Data::Denter as a serializer for comparissons. It supports blessed objects and references, in addition to being able to sort hashes by default. Persoanlly, I think the output is a lot easier on the eyes than trying to look at Dumper output too. If I remember right, it can even handle circular references.

-- KILNA - pop music for cyborgs - http://www.kilna.com

Create A New User
Node Status?
node history
Node Type: perlquestion [id://160765]
Approved by Corion
Front-paged by moodster
help
Chatterbox?
 [thezip]: So I have a script that generates a log file. After script completion, I want tohave VIM open this logfile. [thezip]: i don't get the command line "back" until I close VIM. No what I want to happen... [thezip]: I currently don't have access to CYGWIN, else I'd just do a tail -f on the logfile. [Corion]: thezip: If you want to open vim and can live with opening a second console window, use start "The results" vim.exe c:\path\to\logfile .log [thezip]: Ooops... I lied. I guess Cygwin is back. I'll just do a tail -f instead. Better. Sorry for the noise.

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (12)
As of 2017-03-27 18:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Should Pluto Get Its Planethood Back?

Results (321 votes). Check out past polls.