Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I have two hashes of key-value pairs of numbers. I want to find out how many of these key-value pairs are the same and different in both hashes (matching in both orientations e.g. key-key and key-value etc).

I have some code sort of working but it only works out which keys are different (below). Can someone help me to extend my code to find out which key-value pairs are the same and where the differences are?

Kind regards.

my %old_gp = map {$gp1_a[$_] => $gp1_b[$_]} 0 .. $#gp1_a; my %new_gp = map {$gp2_a[$_] => $gp2_b[$_]} 0 .. $#gp2_a; + + my @diff=(); foreach (keys %new_gp) { push (@diff, "$_ ") unless exists $old_gp{$_}; } + print @diff;

Replies are listed 'Best First'.
Re: comparing key-value pairs of two hashes
by blazar (Canon) on Jul 22, 2005 at 10:50 UTC
    Looking at your code it seems you already know how to pick up array elements. Do you know how to pick up hash elements too? I guess so, then it is fundamentally trivial to perform the remaining test.

    As a side note, if you don't already know it, grep is your friend. For example, assuming you do not really want to store the differing keys as "$_ ", then this should be equivalent to your code:

    @diff=grep { !exists $old_gp{$_} } keys %new_gp;
    And in case you do want to store them that way, then you can chain with a map.
      There does exist also a hash function called "each".
      while (($key,$value) = each %hash) {
      print "$key => $value\n";
      }
      Hope it will help you a little.
Re: comparing key-value pairs of two hashes
by polettix (Vicar) on Jul 22, 2005 at 11:18 UTC
    Warning: All code untested
    Your code for keys is incomplete, because it does not tell you which keys in %old_gp are not in %new_gp. Starting from blazar's suggestion, I would expand:
    my @diff = grep { !exists $old_gp{$_} } keys %new_gp; push @diff, grep { !exists $new_gp{$_} } keys %old_gp;
    More generally:
    my (%foo, %bar); # Populate %foo and %bar # Keys present in only one hash my @only_in_foo = grep { ! exists $bar{$_} } keys %foo; my @only_in_bar = grep { ! exists $foo{$_} } keys %bar; # Matching keys which have differences in values my @kv_different = map { if (exists $bar{$_} && $bar{$_} ne $foo{$_}) { $_ } else { () } } keys %foo; # Matching keys and values my @kv_equal = map { if (exists $bar{$_} && $bar{$_} eq $foo{$_}) { $_ } else { () } } keys %foo;
    More compactly (but not necessarily more efficient or readable, it's a matter of taste):
    my (@only_in_foo, @kv_different, @kv_equal); foreach (keys %foo) { if (exists $bar{$_}) { if ($bar{$_} eq $foo{$_}) { push @kv_equal, $_ } else { push @kv_different, $_ } } else { push @only_in_foo, $_ } } my @only_in_bar = grep { ! exists $foo{$_} } keys %bar;

    Flavio
    perl -ple'$_=reverse' <<<ti.xittelop@oivalf

    Don't fool yourself.
      For those working on their golf (or who just like some of the fun things that Perl can do), here's your exact code in 3 (Perl) lines.
      my (@only_in_foo, @kv_different, @kv_equal); push( @{ exists $bar{$_} ? $bar{$_} eq $foo{$_} ? \@kv_equal : \@kv_different : \@only_in_foo}, $_) for keys %foo; my @only_in_bar = grep { ! exists $foo{$_} } keys %bar;
      Ivan Heffner
      Sr. Software Engineer, DAS Lead
      WhitePages.com, Inc.