Combine hashes with arrays using Hash :: Merge

I am trying to combine two hashes that contains one or more arrays using Hash::Merge . For instance:

 use strict; use warnings; use feature qw(say); use Data::Dump qw(dump); use Hash::Merge qw(merge); my $h1 = { a => [ { aa => 1 }, 3 ] }; my $h2 = { a => [ { bb => 2 } ] }; my $hLeft = merge( $h1, $h2 ); my $hRight = merge( $h2, $h1 ); say " hLeft: " . dump($hLeft); say " hRight: " . dump($hRight); my $hDesired = { a => [ { aa => 1, bb => 2 }, 3 ] }; say "Desired: " . dump($hDesired); 

This gives the result:

  hLeft: { a => [{ aa => 1 }, 3, { bb => 2 }] } hRight: { a => [{ bb => 2 }, { aa => 1 }, 3] } Desired: { a => [{ aa => 1, bb => 2 }, 3] } 

How can I get the correct output using Hash::Merge ?

+5
source share
2 answers

The default behavior for combining arrays is to add them:

 sub { [ @{$_[0]}, @{$_[1]} ] }, 

To get a different behavior, you need to use Hash::Merge::specify_behavior .

The following solution is LEFT_PRECEDENT and combines the array element with the element:

 use strict; use warnings; use feature qw(say); use Data::Dump qw(dump); use Hash::Merge qw(merge); Hash::Merge::specify_behavior( { 'SCALAR' => { 'SCALAR' => sub { $_[0] }, 'ARRAY' => sub { $_[0] }, 'HASH' => sub { $_[0] }, }, 'ARRAY' => { 'SCALAR' => sub { [ @{ $_[0] }, $_[1] ] }, 'ARRAY' => sub { my ( $left, $right ) = @_; my @merged = @$left; my @to_add = @$right; for (@merged) { last if !@to _add; $_ = Hash::Merge::merge( $_, shift @to_add ); } return [ @merged, @to_add ]; }, 'HASH' => sub { [ @{ $_[0] }, values %{ $_[1] } ] }, }, 'HASH' => { 'SCALAR' => sub { $_[0] }, 'ARRAY' => sub { $_[0] }, 'HASH' => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) }, }, }, 'My Behavior', ); my $h1 = { a => [ { aa => 1 }, 3 ] }; my $h2 = { a => [ { bb => 2 } ] }; my $merged = merge( $h1, $h2 ); say "Merged: " . dump($merged); 

Outputs:

 Merged: { a => [{ aa => 1, bb => 2 }, 3] } 
+1
source

This can be done using Hash::Merge::specify_behavior :

 use warnings; use strict; use Data::Dump 'dump'; use Hash::Merge; use feature 'say'; Hash::Merge::specify_behavior ( { 'SCALAR' => { 'SCALAR' => sub { $_[1] }, 'ARRAY' => sub { [ $_[0], @{$_[1]} ] }, 'HASH' => sub { $_[1] }, }, 'ARRAY' => { 'SCALAR' => sub { $_[1] }, 'ARRAY' => \&mergeArrays, 'HASH' => sub { $_[1] }, }, 'HASH' => { 'SCALAR' => sub { $_[1] }, 'ARRAY' => sub { [ values %{$_[0]}, @{$_[1]} ] }, 'HASH' => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) }, }, }, 'My Behavior', ); my $h1={a=>[{aa=>1},3]}; my $h2={a=>[{bb=>2}]}; my $hMerge=Hash::Merge::merge($h1,$h2); say "hMerge: ".dump($hMerge); sub mergeArrays{ my ($a,$b) =@ _; my ($na,$nb)=($#$a,$#$b); my @c; if ($na>$nb) { @ c=@ $a[($nb+1)..$na]; return mergeArrays2($a,$b,\@c,$nb); } else { @ c=@ $b[($na+1)..$nb]; return mergeArrays2($a,$b,\@c,$na); } } sub mergeArrays2{ my ($a,$b,$c,$n) =@ _; my $r=[]; for my $i (0..$n) { if (ref($a->[$i]) && ref($b->[$i])) { push(@$r,Hash::Merge::_merge_hashes($a->[$i],$b->[$i])); } else { push(@$r,$a->[$i]); } } push(@$r,@$c); return $r; } 

Conclusion:

 hMerge: { a => [{ aa => 1, bb => 2 }, 3] } 
+3
source

Source: https://habr.com/ru/post/1205436/


All Articles