How do I do a simple Perl hash equivalence compari

2019-06-16 20:46发布

I'm wondering if there's an idiomatic one-liner or a standard-distribution package/function that I can use to compare two Perl hashes with only builtin, non-blessed types. The hashes are not identical (they don't have equivalent memory addresses).

I'd like to know the answer for both for shallow hashes and hashes with nested collections, but I understand that shallow hashes may have a much simpler solution.

TIA!

8条回答
放荡不羁爱自由
2楼-- · 2019-06-16 20:52

For shallow hashes:

(grep {exists %hash2{$_}} keys %hash1) > 0
查看更多
ら.Afraid
3楼-- · 2019-06-16 20:54

hashes can be casted into arrays, where every value follows its key (but you won't know the order of the keys). So:

( join("",sort(%hash1)) eq join("",sort(%hash2)) )

Oh, wait, that won't work because there are some edge cases, like:

%hash1 = { 'aaa' => 'aa'  };
%hash2 = { 'aa'  => 'aaa' };

So it's best to use a character in the join() that you KNOW will never appear in any key or value. If the values are BLOBs, that will be a big problem, but for anything else you could use the NULL char "\0".

( join("\0",sort(%hash1)) eq join("\0",sort(%hash2)) )

Looks kinda ugly, I know, but it will do for checking if two hashes are equal in a shallow way, which is what most people are looking for.

查看更多
\"骚年 ilove
4楼-- · 2019-06-16 20:55

I don't know if there's an easy way or a built-in package, and I don't know what happens when you just do %hash1 == %hash2 (but that's probably not it), but it's not terribly hard to roll your own:

sub hash_comp (\%\%) {
  my %hash1 = %{ shift };
  my %hash2 = %{ shift };
  foreach (keys %hash1) {
    return 1 unless defined $hash2{$_} and $hash1{$_} == $hash2{$_};
    delete $hash1{$_};
    delete $hash2{$_};
  }
  return 1 if keys $hash2;
  return 0;
}

Untested, but should return 0 if the hashes have all the same elements and all the same values. This function will have to be modified to account for multidimensional hashes.

If you want something from a standard distribution, you could use Data::Dumper; and just dump the two hashes into two scalar variables, then compare the strings for equality. That might work.

There's also a package on CPAN called FreezeThaw that looks like it does what you want.

Note that to use the smart match (not repeated here because it's already posted), you will have to use feature; and it's only available for Perl 5.10. But who's still using Perl 5.8.8, right?

查看更多
一夜七次
5楼-- · 2019-06-16 20:56

Thanks for your question.

I used Test::More::eq_hash as result.

查看更多
劫难
6楼-- · 2019-06-16 20:58

convert hashes to xml files and compare, and yes you could use multilevel.

sub isEqualHash 
{
    my ($self,$hash1, $hash2) = @_;
    my  $file1 = "c:/neo-file1.txt";
    my  $file2 = "c:/neo-file2.txt";
    my $xmlObj = XML::Simple->new();
    my $dummy_file = $xmlObj->XMLout($hash1,OutputFile => $file1);
    my $dummy_file = $xmlObj->XMLout($hash2,OutputFile => $file2);

    open FILE, "<".$file1;
    my $file_contents1 = do { local $/; <FILE> };
    close(FILE);

    open FILE, "<".$file2;
    my $file_contents2 = do { local $/; <FILE> };
    close(FILE);

    if($file_contents1 eq $file_contents2)
    {
        return "Passed";
    }
    else
    {
        return "Failed";
    }
}
查看更多
孤傲高冷的网名
7楼-- · 2019-06-16 21:06

[This was a response to an answer by someone who deleted their answer.]

Uh oh!

%a ~~ %b && [sort values %a] ~~ [sort values %b]

doesn't check whether the values belong to the same keys.

#! perl
use warnings;
use strict;

my %a = (eat => "banana", say => "whu whu"); # monkey
my %b = (eat => "whu whu", say => "banana"); # gorilla
print "Magilla Gorilla is always right\n" 
    if %a ~~ %b && [sort values %a] ~~ [sort values %b];
查看更多
登录 后发表回答