How do I compare two hashes in Perl without using Data::Compare?
The best approach differs according to your purposes. The FAQ item mentioned by Sinan is a good resource: How do I test whether two arrays or hashes are equal?. During development and debugging (and of course when writing unit tests) I have found Test::More
to be useful when comparing arrays, hashes, and complex data structures. A simple example:
use strict;
use warnings;
my %some_data = (
a => [1, 2, 'x'],
b => { foo => 'bar', biz => 'buz' },
j => '867-5309',
);
my %other_data = (
a => [1, 2, 'x'],
b => { foo => 'bar', biz => 'buz' },
j => '867-5309x',
);
use Test::More tests => 1;
is_deeply(\%other_data, \%some_data, 'data structures should be the same');
Output:
1..1
not ok 1 - data structures should be the same
# Failed test 'data structures should be the same'
# at _x.pl line 19.
# Structures begin differing at:
# $got->{j} = '867-5309x'
# $expected->{j} = '867-5309'
# Looks like you failed 1 test of 1.
ok
, cmp_deeply
, etc.) it counts as a test. As far as I know, there isn't a way to avoid that. If you don't want to commit in advance to a specific number of tests, you can do this when you load the testing module: use Test::More qw(no_plan);
. –
Thetes Compare is not a detailed enough phrase when talking about hashes. There are many ways to compare hashes:
Do they have the same number of keys?
if (%a == %b) {
print "they have the same number of keys\n";
} else {
print "they don't have the same number of keys\n";
}
Are the keys the same in both hashes?
if (%a != %b) {
print "they don't have the same number of keys\n";
} else {
my %cmp = map { $_ => 1 } keys %a;
for my $key (keys %b) {
last unless exists $cmp{$key};
delete $cmp{$key};
}
if (%cmp) {
print "they don't have the same keys\n";
} else {
print "they have the same keys\n";
}
}
Do they have the same keys and the same values in both hashes?
if (%a != %b) {
print "they don't have the same number of keys\n";
} else {
my %cmp = map { $_ => 1 } keys %a;
for my $key (keys %b) {
last unless exists $cmp{$key};
last unless $a{$key} eq $b{$key};
delete $cmp{$key};
}
if (%cmp) {
print "they don't have the same keys or values\n";
} else {
print "they have the same keys or values\n";
}
}
Are they isomorphic (I will leave this one up to the reader as I don't particularly want to try implementing it from scratch)?
Or some other measure of equal?
And, of course, this code only deals with simple hashes. Adding complex data structures makes it even more complex.
See How do I test whether two arrays or hashes are equal?
Perl's FAQ and answers are part of you Perl distribution. You can view the version of this answer that came with your perl
by running:
$ perldoc -q equal
in your terminal.
Quick, dirty, and I'm sure not that efficient:
use strict;
use warnings;
use Data::Dumper;
sub compare ($$) {
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 0;
Dumper(shift) eq Dumper(shift);
}
my %a = ( foo => 'bar', bar => [ 0 .. 3 ] );
my %b = ( foo => 'bar', bar => [ 0 .. 3 ] );
my %c = ( foo => 'bar', bar => [ 0 .. 4 ] );
print Dumper compare \%a, \%b;
print Dumper compare \%a, \%c;
Text::Diff
prints a useful report. –
Enterprise local $Data::Dumper::Sortkeys = 1;
to ensure same order of the keys. –
Jenifferjenilee For comparing:
sub HashCompare {
my ( $a, $b ) = @_;
my %rhash_1 = %$a;
my %rhash_2 = %$b;
my $key = undef;
my $hash_2_line = undef;
my $hash_1_line = undef;
foreach $key ( keys(%rhash_2) ) {
if ( exists( $rhash_1{$key} ) ) {
if ( $rhash_1{$key} ne $rhash_2{$key} ) {
print "key $key in $file_1 = $rhash_1{$key} & $rhash_2{$key} in $file_2\n";
}
}
}
else {
print "key $key in $file_1 is not present in $file_2\n";
#next;
}
}
foreach my $comp_key ( keys %rhash_1 ) {
if ( !exists( $rhash_2{$comp_key} ) ) {
print MYFILE "key $comp_key in $file_2 is not present in $file_1\n";
}
}
return;
}
Creating hash with no duplicate keys:
sub CreateHash {
my (@key_val_file ) = @_;
my $key_count = 1;
my %hash_key_val = ();
my $str4 = undef;
local $/ = undef;
foreach my $each_line (@key_val_file) {
@key_val = split( /,/, $each_line );
if ( exists( $hash_key_val{$key_val[0]} ) ) {
$key_count = $key_count + 1;
$str4 = $key_val[0] . " occurence-" . $key_count;
$hash_key_val{$str4} = $key_val[1];
}
else {
$hash_key_val{$key_name} = $key_val[1];
}
}
}
$key_count = 1;
close FILE;
return %hash_key_val;
}
use strict;
use warnings;
use JSON::XS;
my $some_data ={
a => [1, 2, 'x'],
b => { foo => 'bar', biz => 'buz' },
j => '867-5309',
};
my $other_data = {
a => [1, 2, 'x'],
b => { foo => 'bar', biz => 'buz' },
j => '867-5309x',
};
use Test::More tests => 1;
is_deeply(JSON::XS->new->canonical( 1 )->utf8->encode($other_data), JSON::XS->new->canonical( 1 )->utf8->encode($some_data), 'data structures should be the same');
Example
# Failed test 'data structures should be the same'
# at ./t/a.t line 18.
# got: '{"a":[1,2,"x"],"b":{"biz":"buz","foo":"bar"},"j":"867-5309x"}'
# expected: '{"a":[1,2,"x"],"b":{"biz":"buz","foo":"bar"},"j":"867-5309"}'
# Looks like you failed 1 test of 1.
use strict;
use warnings;
use JSON::XS;
my $some_data ={
a => [1, 2, 'x'],
b => { foo => 'bar', biz => 'buz' },
j => '867-5309',
};
my $other_data = {
a => [1, 2, 'x'],
b => { foo => 'bar', biz => 'buz' },
j => '867-5309',
};
use Test::More tests => 1;
is_deeply(JSON::XS->new->canonical( 1 )->utf8->encode($other_data), JSON::XS->new->canonical( 1 )->utf8->encode($some_data), 'data structures should be the same');
Example2
All tests successful.
© 2022 - 2024 — McMap. All rights reserved.