Reputation: 753
I'm trying to merge two hashes and Hash::Merge
does almost exactly what I need, except for arrays. Instead of concatenating arrays I need it to do per-element merge.
For example:
use Hash::Merge qw (merge);
my %a = ( 'arr' => [ { 'a' => 'b' } ] );
my %b = ( 'arr' => [ { 'c' => 'd' } ] );
my %c = %{ merge( \%a, \%b) };
Desired result is ('arr'=>[{'a'=>'b','c'=>'d'}])
, actual result is ('arr'=>[{'a'=>'b'},{'c'=>'d'}])
Can this be done by using specify_behavior
or is there some other way?
Upvotes: 0
Views: 224
Reputation: 6573
Another solution (that handles uneven hash elements in %a and %b).
my %c;
foreach my $key (keys %a, keys %b) {
my $a_ref = $a{$key};
my $b_ref = $b{$key};
$c{$key} = { map %$_, @$a_ref, @$b_ref };
}
use Data::Dumper;
print Dumper \%c;
Upvotes: 0
Reputation: 66873
I think that specify_behaviour
is used to specify how to handle conflicts, or uneven structures to merge. The documentation doesn't actually say much. But try it, go through defined shortcuts, or try to set them yourself. For your data structure you could try
SCALAR => ARRAY => sub { [ %{$_0}, %{$_[0]} ] }
SCALAR => ARRAY => HASH => sub { [ $_[0], $_[0] ] }
If you tried and it didn't work you may have found a bug in the module? By what you show it just didn't go "deep" enough. Here it is without the module. I've enlarged your sample structures.
use warnings;
use strict;
my %a = (
'arr1' => [ { a => 'A', a1 => 'A1' } ],
'arr2' => [ { aa => 'AA', aa1 => 'AA1' } ]
);
my %b = (
'arr1' => [ { b => 'B', b1 => 'B1' } ],
'arr2' => [ { bb => 'BB', bb1 => 'BB1' } ]
);
# Copy top level, %a to our target %c
my %c;
@c{keys %a} = values %a;
# Iterate over hash keys, then through array
foreach my $key (sort keys %c) {
my $arr_len = @{$c{$key}};
foreach my $i (0..$arr_len-1) {
my %hb = %{ ${$b{$key}}[$i] };
# merge: add %b to %c
@{ ${$c{$key}}[$i] }{keys %hb} = values %hb;
}
}
# Print it out
foreach my $key (sort keys %c) {
print "$key: ";
my $arr_len = @{$c{$key}};
foreach my $i (0..$arr_len-1) {
my %hc = %{ ${$c{$key}}[$i] };
print "$_ => $hc{$_}, " for sort keys %hc;
}
print "\n";
}
This prints the contents of %c
(aligned manually here)
arr1: a => A, a1 => A1, b => B, b1 => B1, arr2: aa => AA, aa1 => AA1, bb => BB, bb1 => BB1,
Code does not handle arrays/hashes of unequal sizes but checks can be added readily.
Upvotes: 1