Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge hashes with arrays with Hash::Merge

Tags:

perl

I am trying to merge two hashes which contains one or more arrays using Hash::Merge. For example:

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 output:

  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 ?

like image 243
Håkon Hægland Avatar asked Oct 24 '14 11:10

Håkon Hægland


People also ask

Can arrays be merged?

To merge elements from one array to another, we must first iterate(loop) through all the array elements. In the loop, we will retrieve each element from an array and insert(using the array push() method) to another array. Now, we can call the merge() function and pass two arrays as the arguments for merging.

How do you merge hashes in Ruby?

We can merge two hashes using the merge() method. When using the merge() method: Each new entry is added to the end. Each duplicate-key entry's value overwrites the previous value.

What is array of hashes in Ruby?

A Hash is a dictionary-like collection of unique keys and their values. Also called associative arrays, they are similar to Arrays, but where an Array uses integers as its index, a Hash allows you to use any object type. Hashes enumerate their values in the order that the corresponding keys were inserted.


1 Answers

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

Output:

hMerge: { a => [{ aa => 1, bb => 2 }, 3] }
like image 79
Håkon Hægland Avatar answered Sep 29 '22 22:09

Håkon Hægland