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?.