ハッシュのマージ Hash::Merge

  • ハッシュをマージするには %h3 = (%h1, %h2) で簡単にできる
#!/usr/bin/perl
use strict;
use warnings;
use YAML;
my %hash1 = ( a => 1,
              b => 2,
              c => 3,
            );
my %hash2 = ( a => 1,
              b => 4,
              d => 5,
            );
my %hash3 = (%hash1, %hash2);
print Dump \%hash1;
print Dump \%hash2;
print Dump \%hash3;
__END__
---
a: 1
b: 2
c: 3
---
a: 1
b: 4
d: 5
---
a: 1
b: 4
c: 3
d: 5
  • しかし、ハッシュ構造が複雑な場合、うまくいかない(上書きされてしまう)
#!/usr/bin/perl
use strict;
use warnings;
use YAML;
my %hash1 = ( sv1  => {
                name => "primary-sv",
                addr => "172.16.1.2",
                mask => "255.255.255.0",
              },
              sv2  => {
                name => "secondary-sv",
                addr => "172.16.1.3",
                mask => "255.255.255.0",
              },
            );
my %hash2 = ( sv1 => {
                disk   => "200GB",
                device => "none",
              },
              sv2  => {
                disk   => "500GB",
                device => "DVD",
              },
            );
my %hash3 = (%hash1, %hash2);
print Dump \%hash1;
print Dump \%hash2;
print Dump \%hash3;
__END__
---
sv1:
  addr: 172.16.1.2
  mask: 255.255.255.0
  name: primary-sv
sv2:
  addr: 172.16.1.3
  mask: 255.255.255.0
  name: secondary-sv
---
sv1:
  device: none
  disk: 200GB
sv2:
  device: DVD
  disk: 500GB
---
sv1:
  device: none
  disk: 200GB
sv2:
  device: DVD
  disk: 500GB
  • そこでHash::Mergeを使う
#!/usr/bin/perl
use strict;
use warnings;
use YAML;
use Hash::Merge qw( merge );
my %hash1 = ( sv1  => {
                name => "primary-sv",
                addr => "172.16.1.2",
                mask => "255.255.255.0",
              },
              sv2  => {
                name => "secondary-sv",
                addr => "172.16.1.3",
                mask => "255.255.255.0",
              },
            );
my %hash2 = ( sv1 => {
                disk   => "200GB",
                device => "none",
              },
              sv2  => {
                disk   => "500GB",
                device => "DVD",
              },
            );
my %hash3 = %{ merge(\%hash1, \%hash2) };
print Dump \%hash1;
print Dump \%hash2;
print Dump \%hash3;
__END__
---
sv1:
  addr: 172.16.1.2
  mask: 255.255.255.0
  name: primary-sv
sv2:
  addr: 172.16.1.3
  mask: 255.255.255.0
  name: secondary-sv
---
sv1:
  device: none
  disk: 200GB
sv2:
  device: DVD
  disk: 500GB
---
sv1:
  addr: 172.16.1.2
  device: none
  disk: 200GB
  mask: 255.255.255.0
  name: primary-sv
sv2:
  addr: 172.16.1.3
  device: DVD
  disk: 500GB
  mask: 255.255.255.0
  name: secondary-sv
  • 代わりにこんなサブルーチンでもいける。こっちのがコスト低かも。
#!/usr/bin/perl
use strict;
use warnings;
use YAML;
#use Hash::Merge qw( merge );
my %hash1 = ( sv1  => {
                name => "primary-sv",
                addr => "172.16.1.2",
                mask => "255.255.255.0",
              },
              sv2  => {
                name => "secondary-sv",
                addr => "172.16.1.3",
                mask => "255.255.255.0",
              },
            );
my %hash2 = ( sv1 => {
                disk   => "200GB",
                device => "none",
              },
              sv2  => {
                disk   => "500GB",
                device => "DVD",
              },
            );
#my %hash3 = %{ merge(\%hash1, \%hash2) };
my %hash3 = merge_hash(\%hash1, \%hash2);
print Dump \%hash1;
print Dump \%hash2;
print Dump \%hash3;

sub merge_hash
{
        my ($h1, $h2) = @_;
        my %h3;

        foreach my $hash ($h1, $h2) {
                while (my($key,$val) = each %$hash) {
                        while (my($skey, $sval) = each %$val) {
                                $h3{$key}{$skey} = $sval;
                        }
                }
        }
        return %h3;
}