Reputation: 59
I have to hashes as follows:
my %hash1 = (
'modules' => {
'top0' => {
'instances' => {
'sub_top' => {
'instances' => {
'inst2' => 2,
'inst0' => 0
}
}
}
}
}
);
my %hash2 = (
'modules' => {
'sub_top' => {
'instances' => {
'inst0' => 0,
'inst1' => 1
}
}
}
); }
I need to merge these into one hash. I tried using Hash::Merge but that works only if I start from subtop onwards only.
my $merged_hash = merge(\%{$hash1{modules}{top0}{instances}}, \%{$hash2{modules}});
Dumping the resulting $merged_hash
gives:
$VAR1 = {
'sub_top' => {
'instances' => {
'inst2' => 2,
'inst0' => 0,
'inst1' => 1
}
}
};
But I miss the top part of hash1:
'modules' => {
'top0' => {
'instances' => {
Desired hash after merging should be like this:
$VAR1 = {
'modules' => {
'top0' => {
'instances' => {
'sub_top' => {
'instances' => {
'inst2' => 2,
'inst0' => 0,
'inst1' => 1
}
}
}
}
}
};
Upvotes: 0
Views: 134
Reputation: 6798
One of possible ways to merge hashes on predefined key. See if it satisfies your expectation.
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my $debug = 0;
my %hash1 = (
'modules' => {
'top0' => {
'instances' => {
'sub_top' => {
'instances' => {
'inst2' => 2,
'inst0' => 0
}
}
}
}
}
);
my %hash2 = (
'modules' => {
'sub_top' => {
'instances' => {
'inst0' => 0,
'inst1' => 1
}
}
}
);
my $key = 'sub_top';
mergeOnKey(\%hash1, \%hash2, $key);
say Dumper(\%hash1);
sub mergeOnKey {
my $h1 = shift;
my $h2 = shift;
my $k = shift;
my $href1 = find_ref($h1,$k);
my $href2 = find_ref($h2,$k);
die 'Hash 1 no key was found' unless $href1;
die 'Hash 2 no key was found' unless $href2;
merge($href1,$href2);
}
sub merge {
my $href1 = shift;
my $href2 = shift;
foreach my $key (keys %{$href2}) {
if( ref $href2->{$key} eq ref {} ) {
merge($href1->{$key},$href2->{$key});
} else {
$href1->{$key} = $href2->{$key} unless defined $href1->{$key};
}
}
say Dumper($href1) if $debug;
}
sub find_ref {
my $href = shift;
my $key = shift;
while( my($k,$v) = each %{$href} ){
say Dumper($v) if $debug;
return $v if $k eq $key;
return find_ref($v,$key) if ref $v eq 'HASH';
}
return 0;
}
Output
$VAR1 = {
'modules' => {
'top0' => {
'instances' => {
'sub_top' => {
'instances' => {
'inst1' => 1,
'inst2' => 2,
'inst0' => 0
}
}
}
}
}
};
Upvotes: 3
Reputation: 11158
If you look at the Synopsis for Hash::Merge, the example shows two hashes that look practically identical in structure. The hashes that you want to merge may have many similarities, but they also have significant differences. So, I would only expect that you can only merge them where their structure is sufficiently "equivalent". Here is the most obvious way that I can think of to get them to merge to the resulting hash you indicate:
#!/bin/env perl
use strict;
use warnings;
use Test::More;
use Hash::Merge qw(merge);
my %hash1 = (
'modules' => {
'top0' => {
'instances' => {
'sub_top' => {
'instances' => {
'inst2' => 2,
'inst0' => 0
}
}
}
}
}
);
my %hash2 = (
'modules' => {
'sub_top' => {
'instances' => {
'inst0' => 0,
'inst1' => 1
}
}
}
);
# Merge the desired parts of the structure
$hash1{modules}{top0}{instances} =
merge( \%{ $hash1{modules}{top0}{instances} }, \%{ $hash2{modules} } );
# Demonstrate that merge worked as desired
my %desired = (
'modules' => {
'top0' => {
'instances' => {
'sub_top' => {
'instances' => {
'inst2' => 2,
'inst0' => 0,
'inst1' => 1
}
}
}
}
}
);
is_deeply( \%hash1, \%desired, 'Desired hash is created' );
done_testing();
Upvotes: 1