Reputation: 2498
I want to compare two hash of arrays to see if they are equal. That is, the key values should contain the same elements.
my %h1 = (
w1 => ['3','1','2'],
e2 => ['6','2','4'],
r1 => ['8', '1'],
);
my %h2 = (
w1 => ['3','1','2'],
e2 => ['6','2','4'],
r1 => ['8','1'],
);
my %h3 = (
w1 => ['3','1','2'],
e2 => ['6','2','4'],
r1 => ['4','1'],
);
my $i = 0;
foreach ( keys %h2 ){
my $conditional = scalar keys %h2; # 3
if ([sort @{$h1{$_}}] ~~ [sort @{$h2{$_}}]) {
$i++;
}
if ($i eq $conditional){
print "true\n";
}
}
Comparing %h1
and %h2
should return true since they are equal. Comparing %h1
and %h3
prints nothing. 1) Is there a better way of doing this?
EDIT
I would like to do this without using a function from a module.
Upvotes: 3
Views: 3132
Reputation: 186
I'd create separate subs for each operation: test if arrays are equal and same for hashes:
sub is_arrays_equal($$) {
my ($a, $b) = @_;
if (scalar @$a != scalar @$b) {
return 0;
}
for my $i (0 .. $#{$a}) {
if ($a->[$i] ne $b->[$i]) {
return 0;
}
}
return 1;
}
sub is_hoa_equal($$) {
my ($a, $b) = @_;
if (scalar(keys %$a) != scalar(keys %$b)) {
return 0;
}
for my $k (keys %$a) {
if (exists $b->{$k}) {
if (ref $a->{$k} ne 'ARRAY' || ref $b->{$k} ne 'ARRAY' || !is_arrays_equal($a->{$k}, $b->{$k})) {
return 0;
}
}
else {
return 0;
}
}
return 1;
}
Note: change 'ne' to '!=' or add logics to compare your scalar values.
Your solution is not good because of sorting each array and other heavy data processing. If your need equality test for all types of hashes (not only for HoA as in my solution) just add scalar test and hash test, which will be recursive invocation of main sub, something like that:
sub arrays_equal {
my ( $a, $b ) = @_;
if ( scalar @$a != scalar @$b ) {
return 0;
}
for my $i ( 0 .. $#{$a} ) {
my $va = $a->[$i];
my $vb = $b->[$i];
if ( ref $va ne ref $vb ) {
return 0;
}
elsif ( ref $va eq 'SCALAR' && $va ne $vb ) {
return 0;
}
elsif ( ref $va eq 'ARRAY' && !arrays_equal( $va, $vb ) ) {
return 0;
}
elsif ( ref $va eq 'HASH' && !hashes_equal( $va, $vb ) ) {
return 0;
}
}
return 1;
}
sub hashes_equal {
my ( $a, $b ) = @_;
if ( scalar( keys %$a ) != scalar( keys %$b ) ) {
return 0;
}
for my $k ( keys %$a ) {
if ( exists $b->{$k} ) {
my $va = $a->{$k};
my $vb = $b->{$k};
if ( ref $va ne ref $vb ) {
return 0;
}
elsif ( ref $va eq 'SCALAR' && $va ne $vb ) {
return 0;
}
elsif ( ref $va eq 'ARRAY' && !arrays_equal( $va, $vb ) ) {
return 0;
}
elsif ( ref $va eq 'HASH' && !hashes_equal( $va, $vb ) ) {
return 0;
}
}
else {
return 0;
}
}
return 1;
}
This code checks scalars as strings. You can add is_number or smth like that or just change 'ne' to '!=' (but beware of warnings if there will be not only numbers).
Upvotes: 1
Reputation: 118605
Data::Dumper
is a core module that serializes data structures. Then you can test the serializations for string equality.
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
if (Dumper(\%hash1) eq Dumper(\%hash2)) {
print "hashes are equal\n";
}
If even Data::Dumper
is off-limits, write your own serialization routine that converts an HoA to a string, and compare the strings. Just make sure you don't get tripped up by the randomish order that hash keys are returned in (that's what the $Data::Dumper::Sortkeys=1
line is for in the example above).
Upvotes: 3
Reputation: 2316
Try Test::Deep::NoTest, which handles everything Test::Deep does, except it just returns true or false without outputing a TAP test.
print "true" if eq_deeply(\%h1, \%h2)
Upvotes: 4