4

There is a question that explains exactly what I want here: how to merge 2 deep hashes in perl

However, the answer there does not seem to work for me (suggestions of using the Merge module).

I have two hashes like so:

$VAR1 = {
          '57494' => {
                       'name' => 'John Smith',
                       'age' => '9',
                       'height' => '120'
                     },
          '57495' => {
                       'name' => 'Amy Pond',
                       'age' => '17',
                       'height' => '168'
                     }
        }
};
$VAR1 = {
          '57494' => {
                       'name_address' => 'Peter Smith',
                       'address' => '5 Cambridge Road',
                       'post_code' => 'CR5 0FS'
                     }
        }
};

If I use Hash::Merge or the %c = {%a,%b} format I get this every time:

$VAR1 = '57494';
$VAR2 = {
          'name_address' => 'Peter Smith',
          'address' => '5 Cambridge Road',
          'post_code' => 'CR5 0FS'
        };

(so it basically overwrote the first data with the second and messed up the keys) when I want:

$VAR1 = {
          '57494' => {
                       'name' => 'John Smith',
                       'age' => '9',
                       'height' => '120'
                       'name_address' => 'Peter Smith',
                       'address' => '5 Cambridge Road',
                       'post_code' => 'CR5 0FS'
                     },
          '57495' => {
                       'name' => 'Amy Pond',
                       'age' => '17',
                       'height' => '168'
                     }
        }
};

So when the keys are the same, the data merges together, otherwise the new keys are just appended onto the end. I hope this make sense. Maybe I've done something incorrectly using Merge or need to 'manually' add them in loops, but I'm spending too much time thinking about it, regardless!

Edit: how I use Merge to see if I'm doing something silly:

I have:

use Hash::Merge qw( merge );

...hash data above as %hash1 and %hash2...

my %combined_hash = %{ merge( %hash1,%hash2 ) };
print Dumper(%combined_hash);
Community
  • 1
  • 1
dgBP
  • 1,681
  • 6
  • 27
  • 42
  • 1
    I don't. I get your desired result using either [**`Hash::Merge`**](http://search.cpan.org/perldoc?Hash::Merge) or [**`Hash::Merge::Simple`**](http://search.cpan.org/perldoc?Hash::Merge::Simple). – Axeman Nov 06 '12 at 12:37
  • There are too many `}` in your example data... – simbabque Nov 06 '12 at 12:50
  • If there are it is a copy and paste error. I copied from Dumper print out. – dgBP Nov 06 '12 at 12:52
  • @Axeman if it worked for you, may I ask how you did it? It would be useful to know... – dgBP Nov 06 '12 at 12:52
  • See my answer for what @Axeman probably did. I guess he just read the docs ;-) – simbabque Nov 06 '12 at 12:57

2 Answers2

7

If I do it with references, it works like a charm.

use strict; use warnings;
use Data::Dumper;
use Hash::Merge qw(merge);
my $h1 = {
  '57494' => {
    'name'   => 'John Smith',
    'age'    => '9',
    'height' => '120'
  },
  '57495' => {
    'name'   => 'Amy Pond',
    'age'    => '17',
    'height' => '168'
  }
};

my $h2 = {
  '57494' => {
    'name_address' => 'Peter Smith',
    'address'      => '5 Cambridge Road',
    'post_code'    => 'CR5 0FS'
  }
};

my $h3 = merge( $h1, $h2 );
print Dumper $h3;

Output:

$VAR1 = {
      '57495' => {
                   'name' => 'Amy Pond',
                   'age' => '17',
                   'height' => '168'
                 },
      '57494' => {
                   'name_address' => 'Peter Smith',
                   'name' => 'John Smith',
                   'post_code' => 'CR5 0FS',
                   'address' => '5 Cambridge Road',
                   'height' => '120',
                   'age' => '9'
                 }
    };

If, however, I do it with hashes instead of hash refs, it doesn't:

my %hash1 = (
  '57494' => {
    'name'   => 'John Smith',
    'age'    => '9',
    'height' => '120'
  },
  '57495' => {
    'name'   => 'Amy Pond',
    'age'    => '17',
    'height' => '168'
  }
);

my %hash2 = (
  '57494' => {
    'name_address' => 'Peter Smith',
    'address'      => '5 Cambridge Road',
    'post_code'    => 'CR5 0FS'
  }
);

my %h3 = merge( %hash1, %hash2 );
print Dumper \%h3;

__END__
$VAR1 = {
  '57495' => undef
};

That is because the merge from Hash::Merge can only take references, but you are passing it hashes. In addition, you need to call it in scalar context.

Try it like so:

#                             +--------+--- references
#   ,-- SCALAR context        |        |
my $combined_hash = %{ merge( \%hash1, \%hash2 ) };
print Dumper($combined_hash);
simbabque
  • 53,749
  • 8
  • 73
  • 136
  • ahh you know, I have my original versions as references but did this: `my %h3 = %{ merge( $h1, $h2 )};` but when I changed it to pure references, it seems to work. Weird! I feel a little silly that I missed trying that combination, but thanks for making it clear to me! – dgBP Nov 06 '12 at 12:57
  • @bladepanthera There was an error in it. The key (not the hash key) is to reference the two argument hashes, but to call it in scalar context. You need `$combined_hash`, not `%combined_hash`. Then you can reference the hashes you pass to `merge` and do not need to put them in reference variables. – simbabque Nov 06 '12 at 13:00
1
for my $key (keys %fromhash) {
    if(not exists $tohash{$key}) {
        $tohash{$key} = {};
    }
    for my $subkey (keys %{$fromhash{$key}}) {
        ${$tohash{$key}}{$subkey} = ${$fromhash{$key}}{$subkey};
    }
}

With more or less braces depending on whether my last coffee was any good.

Python is definitely more comfortable for this kind of thing, because it doesn't make you think about references:

for key in fromdict:
    if key not in todict:
        todict[key] = {}
    todict[key] = dict(fromdict[key].items() + todict[key].items())

Or if todict is a defaultdict (creating keys on read as well as assignment):

for key in fromdict:
    todict[key] = dict(dict(fromdict[key]).items() + dict(todict[key]).items())
Phil H
  • 19,928
  • 7
  • 68
  • 105
  • I don't get the coffee reference. – simbabque Nov 06 '12 at 12:48
  • 2
    Then you haven't had enough coffee either. – Phil H Nov 06 '12 at 13:11
  • 2
    In fact there's one more brace required: `keys %{$fromhash{$key}})` in line 5. I'll stick to Yorkshire Tea though. =) – simbabque Nov 06 '12 at 13:15
  • @Phil H, your perl code is working perfectly. But I am not able to understand the line no.3. My understanding is, it assigns an empty anonymous hash if the key is present in `%fromhash` and is absent in `%tohash`. But it is doing the correct thing by adding that key to `%tohash` and I am wrong. So could you please explain the logic on line no. 3. Thanks. – slayedbylucifer Nov 06 '12 at 14:16
  • 1
    Without `$key` existing in `%tohash`, the assigment to that hash in the inner `for` loop would fail. So we create it if it is not defined by assigning it an empty hash reference. – Phil H Nov 06 '12 at 14:23