Search code examples
perlhashmergehash-of-hashes

Combining 2+ 'deep' (multi-dimensional) hashes in perl


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);

Solution

  • 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);