Tim
Tim

Reputation: 214

Best way to compare multi-dimension hash in perl

I want to write a script that can compare two multi-dimension hash to see whether they match or not. This two hash has same value of key, the comparison will done in a pair mean starting with $j=0 first compare $line[0] $line[1] then follow by $line[2] $line[3]

$hash{"key"}{$key}{"$row $col"}= "$line[$j] $line[$j+1]";
$hash1{"key"}{$key1}{"$row1 $col1"}="$line1[$j] $line1[$j+1]";

my $line and $line1 is take from a huge file and will process the huge file line by line. For example:

@line = 1 2 3 4 5 6 7 8 #fst line from file1
@line1 = 1 2 3 3 4 5 6 7 7 #fst line from file2

when $key and $key1 match the scripts will continue to compare the $row $col then lastly compare $line[$j] $line[$j+1]. I am trying to use one of the module Test::More that mention at previous similar question but it can only compare the 1st not matching and the output is in default format.

not ok 1 - data structures should be the same
#   Failed test 'data structures should be the same'
#   at hash_check1.pl line 80.
#     Structures begin differing at:
#          $got->{key}{100}{2 3} = '62 19'
#     $expected->{key}{100}{2 3} = '12 24'
# Tests were run but no plan was declared and done_testing() was not seen.

Any best way to compare this type of multi-dimension hash?

8/22 Edited

If Test::More module is able to compare all the $key match for two hash i can take that as well but if i can output the matching and non matching in my own prefer format will be great. For example i would like to output like

$key at $row $col no match with value $line[$j] $line[$j+1] ( expected value $line1[$j] $line1[$j+1] )

8/22 Edited

Below is part of my code

use strict;
use warnings;
use Test::More;

open ( FILE1 , '<', "file.txt" ) or die $!;
open ( FILE2 , '<' , "file1.txt" ) or die $!;

chomp (my @file1 = <FILE1>);
chomp (my @file2 = <FILE2>);
my %hash=();
my %hash1=();

for ( $i =0 ; $i<=511 ; $i++ ) {
    my @line = split(" ",$file1[$i]);
    my @line1 = split(" ",$file2[$i]);
    my $key = ($i+1)*10;
    my $key1 = ($i+2)*10;

    for ( $j=0; $j<=15 ; $j+=2){
          my $col = hex($j);
          my $col1 = hex($j+1);
          $hash{"key"}{$key}{"$row $col1"}= "$line[$j] $line[$j+1]";
          $hash1{"key"}{$key1}{"$row1 $col1"}= "$line1[$j] $line1[$j+1]";
    }
}

##############comparison part start here###################
is_deeply(\%hash, \%hash1, 'data structures should be the same'); #can only print one mismatch

################Any better way?###########

Upvotes: 2

Views: 496

Answers (1)

H&#229;kon H&#230;gland
H&#229;kon H&#230;gland

Reputation: 40718

Here is an example that will compare nested hashes with string values:

#! /usr/bin/env perl

use feature qw(say);
use warnings;
use strict;

my %hash;
my %hash1;

$hash{"key"}{A}{"2 3"}= "1 2";
$hash1{"key"}{A}{"2 3"}="2 3";
$hash{"key"}{B}{"2 3"}= "1 2";
$hash1{"key"}{C}{"2 3"}="2 3";

compare(\%hash, \%hash1);

sub compare {
    return _compare( @_, "" );
}

sub get_key_str { return $_[0] . '{' . $_[1] . '}' }

sub _process_key {
    my ( $h1, $h2, $info, $key, $str1, $str2 ) = @_;

    if ( exists $h2->{$key} ) {
        my $val1 = $h1->{$key};
        my $val2 = $h2->{$key};
        if ( ref $val1 eq "HASH" and ref $val2 eq "HASH" ) {
            _compare( $val1, $val2, get_key_str( $info, $key ) );
        }
        else {
            die "Expected string value" if ref $val1 or ref $val2;
            if ( $val1 ne $val2 ) {
                say "Value '$val1' in $str1 hash for key " . get_key_str( $info, $key )
                  . " does not match value '$val2' in $str2 hash";
            }
        }
    }
    else {
        my $cur_key = get_key_str( $info, $key );
        say "Got key $cur_key in $str1 hash, but missing in $str2 hash"; 
    }
}


sub _compare {
    my ( $h1, $h2, $info ) = @_;

    my %processed_keys;
    for (keys %$h1) {
        _process_key( $h1, $h2, $info, $_, "first", "second" );
        $processed_keys{$_}++;
    }
    for (keys %$h2) {
        next if exists $processed_keys{$_};
        _process_key( $h2, $h1, $info, $_, "second", "first" );
    }
}

Output:

Got key {key}{B} in first hash, but missing in second hash
Value '1 2' in first hash for key {key}{A}{2 3} does not match value '2 3' in second hash
Got key {key}{C} in second hash, but missing in first hash

Upvotes: 2

Related Questions