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

Dear Monks

I'm trying to compare two Hash ref's of hashes and trying to get the Union, Intersection of two hash ref's, but unable to proceed further.

I tried Data::Compare module to compare my data structures, but unable to get the Union & Intersection. Help me out

use strict; use Data::Dumper; use Data::Compare; print "Hello, World...\n"; my $old = { '2-43' => { 'transid' => '114337799', 'vrec' => 'NAM03', 'taxamt_vcd' => '0', 'hub' => 'HUB11', 'seqnum' => '43', 'dch_detail_recid' => '229658296', 'sdrpretax_vcd' => '10', 'transid_recpay' => '114337799z', 'dchdetaildate' => '06-JUN-10', 'sdrposttax_vcd' => '10', 'vpay' => 'GINCL', 'virtual_agree_id' => '2' }, }; my $new = { '2-43' => { 'transid' => '114337799', 'vrec' => 'NAM03', 'taxamt_vcd' => '0', 'hub' => 'HUB11', 'seqnum' => '43', 'dch_detail_recid' => '229658296', 'sdrpretax_vcd' => '10', 'transid_recpay' => '114337799z', 'dchdetaildate' => '06-JUN-10', 'sdrposttax_vcd' => '10', 'vpay' => 'GINCL', 'virtual_agree_id' => '2' }, '2-44' => { 'transid' => '114337799', 'vrec' => 'NAM03', 'taxamt_vcd' => '0', 'hub' => 'HUB11', 'seqnum' => '44', 'dch_detail_recid' => '229658296', 'sdrpretax_vcd' => '10', 'transid_recpay' => '114337799z', 'dchdetaildate' => '06-JUN-10', 'sdrposttax_vcd' => '10', 'vpay' => 'GINCL', 'virtual_agree_id' => '2' }, };

Replies are listed 'Best First'.
Re: Compare Two Hash refs of Hashes
by jethro (Monsignor) on Jul 15, 2010 at 09:42 UTC
    Union and Intersection operate on sets. If you want to operate on hashes you first have to define what you mean by that. Is it the intersection of the keys, the values or both? What would be the union of 'seqnum'=>43 and 'seqnum'=>44 (as you can't store two values with the same key) ?

      Here is my requirement and basically want to update the Database

      I want all the keys and values from the hash refs.

      Compare $old and $new and

      ~ get any new keys which should be INSERTED into DB

      ~ get common keys, but again check for any changes in their values. If so UPDATE DB

      ~ get the missing keys in $new with that of $old. If so, DELETE from DB

        # Throw away all keys in old hash that are in new, update keys that ch +anged or are new foreach $key (keys %$new) { if (exists($old->{$key}) { if ($old->{$key} ne $new->{$key}) { updateindatabase($key,$new->{$key}); } delete $old->{$key}; } else { updateindatabase($key,$new->{$key}); } # remove keys that are only in old foreach $key (keys %$old) { deleteindatabase($key); }

        That should do it. I assume the hash is very big, otherwise simply deleting and just storing the new hash would be faster and simpler

        what about this straight forward approach?

        while ( ($ko,$vo) = each %$old ) { while ( ($kn,$vn) = each %$new ) { # compare } }

        of course there are other tricks possible like using hash slices or array slices of keys of hashes and so on...

        Cheers Rolf

Re: Compare Two Hash refs of Hashes
by LanX (Saint) on Jul 15, 2010 at 11:20 UTC
    here some basics for union, intersection and diff of two arrays. The keys and values of hashes can be treated as arrays.

    > perl -de0 ... DB<1> @a=0..6 DB<2> @b=3..9 DB<3> print grep { exists $a[$_] } @b # intersection 3456 DB<4> print grep { ! exists $a[$_] } @b # missing in @a 789 DB<5> @union{@a,@b}=() DB<6> print keys %union # union 6379281405

    UPDATE: hmm maybe should be added that exists also works with hashes:

    grep { exists $a{$_} } keys %b     # keys intersection

    and with refs it's

    grep { exists $ar->{$_} } keys %$br     # keys intersection

    Cheers Rolf