kathy
kathy

Reputation: 37

Merging perl array which contains hashes in it

I am trying to do Merging in perl where array which contains hashes in it, I am trying to not merge duplicate elements(via uniq function) in an array while merging.

use strict;
use Hash::Merge qw/merge/;
use List::MoreUtils qw/uniq/;
use Data::Dumper;

my $h1 = { name1    => [ {
                           sport  => "tennis",
                           country => "us",
                          },
                          {
                           sport => "soccer",
                           country => "brazil",
                          },
                          {
                           sport  => "cricket",
                           country => "india"
                          },
                        ]
        };

my $h2 = { name1 => [   {
                           sport => "soccer",
                           country => "brazil",
                          },

                        {
                           sport  => "boxing",
                           country => "china"
                         }
                          ],

        };

Output:-

$VAR1 = {
      'name1' => [
                   {
                     'country' => 'us',
                     'sport' => 'tennis'
                   },
                   {
                     'country' => 'brazil',
                     'sport' => 'soccer'
                   },
                   {
                     'sport' => 'cricket',
                     'country' => 'india'
                   },
                   {
                     'country' => 'brazil',
                     'sport' => 'soccer'
                   },
                   {
                     'country' => 'china',
                     'sport' => 'boxing'
                   }
                 ]
    };

When I try to merge with custom merge method, I am not able to eliminate duplicate elements ({ sport => "soccer" country => "brazil" }). Seems like reason is because of elements in hash format. Do we have any easy way using custom merge technique to achieve it. Thanks!

Upvotes: 2

Views: 96

Answers (1)

Håkon Hægland
Håkon Hægland

Reputation: 40718

You can try to flatten the sub hashes in the arrays in order to be able to apply uniq to them. Then expand the sub hashes back after removing the duplicates. For example:

Hash::Merge::specify_behavior(
  {
    SCALAR => {
        SCALAR => sub { $_[1] },
        ARRAY  => sub { [ $_[0], @{$_[1]} ] },
        HASH   => sub { $_[1] },
    },
    ARRAY => {
        SCALAR => sub { $_[1] },
        ARRAY  => sub { my_array_merge( $_[0], $_[1] ) },
        HASH   => sub { $_[1] },
    },
    HASH => {
        SCALAR => sub { $_[1] },
        ARRAY  => sub { [ values %{$_[0]}, @{$_[1]} ] },
        HASH   => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) },
    },
  },
  'setting merge',
);


my $h_data = merge($h1, $h2);

print Dumper $h_data;


sub flatten_array_of_hash {
    my ( $ar ) = @_;

    my @flat;
    for my $hash (@$ar) {
        die "Not hash ref" if !(ref $hash eq "HASH");
        my @ar;
        for my $key (sort keys %$hash) {
            push @ar, join $;, $key, $hash->{$key};
        }
        push @flat, join $;, @ar;
    }
    return \@flat;
}

sub my_array_merge {
    my ($a1, $a2) = @_;

    my $f1 = flatten_array_of_hash( $a1 );
    my $f2 = flatten_array_of_hash( $a2 );
    my @flat = uniq @$f1, @$f2;
    my @result;
    for my $item (@flat) {
        my %hash = split $;, $item;
        push @result, \%hash;
    }
    return \@result;
}

Upvotes: 1

Related Questions