Unique arrays in a multidimensional array

132 Views Asked by At

I have a multidimensional array and I'm looking for a way to get only unique elements in the array.

use strict;
use feature 'say';
use List::Util 'shuffle';
#use List::MoreUtils 'uniq';

my @array = shuffle (
    [4, 10],
    [5, 6],
    [1, 2],
    [1, 2],
    [1, 2]
);

sub uniq {
    my %seen;
    grep !$seen{$_}++, @_;
};
my @unique = uniq(@array); 
foreach (@unique) {say "@$_";};

And this doesn't work. It seems like each array in a multidimensional array is a different reference.

I tried to use uniq from MoreUtils but it also doesn't work.

Please help.

P/s: I'm looking for a way to produce [4, 10] [5, 6] [1, 2].

1

There are 1 best solutions below

14
clamp On

You have to reference both array values in your %seen hash:

#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use List::Util 'shuffle';


my @array = shuffle (
    [4, 10],
    [5, 6],
    [1, 2],
    [1, 2],
    [1, 2]
);

sub uniq {
    my %seen;
    grep !$seen{$_->[0]}{$_->[1]}++, @_;
};
my @unique = uniq(@array); 
foreach (@unique) {say "@$_";};

Instead of a nested hash, you could stringify the two elements like so:

grep !$seen{"$_->[0],$_->[1]"}++, @_;

For the general case of an array of arbitrary data structures you can serialize the elements. This example uses Storable::freeze for serialization. $Storable::canonical needs to be set to true to allow for comparison of data structures. See documentation. As @Ikegami points out, this will still cause problems with float values...

#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use List::Util 'shuffle';
use Data::Dumper;
use Storable qw/freeze/;
#use List::MoreUtils 'uniq';

my @array = shuffle (
    [4, 10],
    [5, 6],
    [1, 2],
    [1, 2],
    [1, 2],
    [1,2,3],
    [1,2,{foo=>'bar'}],
    [1,2,{foo=>'bar'}],
    [1,2,{foo=>'baz'}],
);

sub uniq {
    my %seen;
    local $Storable::canonical = 1;
    grep !$seen{freeze( $_ )}++, @_;
};
my @unique = uniq(@array); 
say Dumper \@unique;