Home > Net >  perl - Hash::Merge duplicates same list within hashes instead of putting them once
perl - Hash::Merge duplicates same list within hashes instead of putting them once

Time:09-17

I'm trying to merge two hashes that hold lists inside them. The thing is that those lists are exactly the same, but because they are lists, the merger duplicates their values inside.

Any ideas how can I remove the duplication?

#!usr/bin/perl
use strict;
use warnings;
use Hash::Merge;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;

my $hash1 = {
                 
                 'Instance' => [ 1,2 ]
                 
               };
my $hash2 = {
                 'Instance' => [ 1,2 ] 
    };


my $merger = Hash::Merge->new('LEFT_PRECEDENT');    
my $hash3 = $merger->merge($hash2, $hash1);
print Dumper($hash3);

The output:

$VAR1 = {
          'Instance' => [
                          1,
                          2,
                          1,
                          2
                        ]
        };

What I want is:

$VAR1 = {
          'Instance' => [
                          1,
                          2
                        ]
        };

AFTER EDIT: I posted a continuing question.

CodePudding user response:

As often when you want to do something a bit advanced with Hash::Merge, the answer is "implement your own custom behavior".

In this case, you can do:

my $merger = Hash::Merge->new('LEFT_PRECEDENT');
my $behavior = $merger->get_behavior_spec($merger->get_behavior);
$behavior->{ARRAY}{ARRAY} = sub {
    my ($left, $right) = @_;
    my %seen = map { $_ => 1 } @$left;
    return [ @$left, grep { ! $seen{$_} } @$right ];
};

my $hash3 = $merger->merge($hash2, $hash1);

Where the line my %seen = map { $_ => 1 } @$left; populates the hash %seen with the values of the $left array, and grep { ! $seen{$_} } @$right filters the $right array by keeping only the values that are not in %seen.

Note that this approach does not remove all duplicates: if $left or $right contain duplicate elements (eg, if $left = [1, 1, 2]), then those duplicates will remain. If you want to remove all duplicates, then use this version instead:

use List::MoreUtils qw(uniq);
$behavior->{ARRAY}{ARRAY} = sub {
    my ($left, $right) = @_;
    return [ uniq @$left, @$right ];
};

If, for any reason, you don't want to rely on List::MoreUtils for the uniq function, you can easily implement your own: How do I remove duplicate items from an array in Perl?.

  • Related