neversaint has asked for the wisdom of the Perl Monks concerning the following question:
Dear Masters,
I am trying to find unique array of Array
with this code:
use List::MoreUtils qw/uniq/;
my @AoA = (['a','b','c'],
['a','b','c'],
['a','b','d'],
['a','b','d']);
my @uAoA = uniq(@AoA);
print Dumper \@uAoA;
But it fails to return the desired result:
$VAR1 = [
['a','b','c],
['a','b','d']
];
Why is it? Is there a quick way to achieve that?
---
neversaint and everlastingly indebted.......
Re: Fast Way to Return Unique Array of Array
by pg (Canon) on Sep 05, 2005 at 03:30 UTC
|
This is not what the function is supposed to do. Take a look at the source code. It only understands the first level of your AoA. Copy and paste, modify a little bit:
use Data::Dumper;
my @AoA = (['a','b','c'],
['a','b','c'],
['a','b','d'],
['a','b','d']);
my @uAoA = uniq(@AoA);
sub uniq () { #updated, the prototype was taken out
my %h;
map { $h{$_}++ == 0 ? $_ : () } @_;
print Dumper \%h;
}
Run it , you get print out similar to this:
$VAR1 = {
'ARRAY(0x224ff8)' => 1,
'ARRAY(0x18c3a2c)' => 1,
'ARRAY(0x1875904)' => 1,
'ARRAY(0x224ef0)' => 1
};
Now it is obvious, all those four elements of the LIST are different and uniqe, and that's why the entire original AOA is returned.
Update:
Just to extend a little bit. uniq will work with this code, base on the way the AoA is formed. The input AoA is logically equal to your AoA. And the output is now the same as what you wanted. (I am not saying that this is a solution for you, as you probably cannot form your data like this or cannot do it easily. This only demos the nature of uniq(). )
use Data::Dumper;
use List::MoreUtils qw(uniq);
use strict;
use warnings;
my $a = ['a','b','c'];
my $b = ['a','b','d'];
my @AoA = ($a, $a, $b, $b);
my @uAoA = uniq(@AoA);
print Dumper(\@uAoA);
Why does it work? Because the uniq function sees two uniq array refs.
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Fast Way to Return Unique Array of Array
by Zaxo (Archbishop) on Sep 05, 2005 at 03:32 UTC
|
The array references in @AoA are to four distinct locations, so they are all unique, even though their contents may be equal.
We've shown you a bunch of ways to get uniqueness, mostly having to do with hash keys. You can form a suitable key from one of these array references with join ',', @{$AoA[$idx]}.
| [reply] [Watch: Dir/Any] [d/l] |
Re: Fast Way to Return Unique Array of Array
by GrandFather (Saint) on Sep 05, 2005 at 04:09 UTC
|
This may be what you are trying to achieve, though it's not very pretty:
use strict;
use warnings;
use List::Compare::Functional qw(is_LequivalentR);
use Data::Dumper;
my @AoA = (['a','b','c'],
['a','b','c'],
['a','b','d'],
['a','b','d']);
my @uAoA;
skip: for my $ia (0..(@AoA-1)){
for my $ir (0..(@uAoA-1)){
next skip if is_LequivalentR ([\@{$AoA[$ia]}, \@{$uAoA[$ir]}]);
}
push @uAoA, [@{$AoA[$ia]}];
}
print Dumper \@uAoA;
Outputs:
$VAR1 = [
[
'a',
'b',
'c'
],
[
'a',
'b',
'd'
]
];
Perl is Huffman encoded by design.
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Fast Way to Return Unique Array of Array
by monkfan (Curate) on Sep 05, 2005 at 03:58 UTC
|
This is not a neat code, but does the job.
Inspired from good Zaxo's comment to you above:
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my $uAoA = uniqAoA(\@AoA);
print Dumper $uAoA;
sub uniqAoA
{
my $array = shift;
my %res;
foreach my $ar (@{$array})
{
my $str = join (",",@{$ar});
$res{$str} = 1;
}
my @kys = keys(%res);
my @final;
foreach(@kys)
{
my @a = split(",",$_);
push @final, [ @a ];
}
return \@final;
}
| [reply] [Watch: Dir/Any] [d/l] |
|
use strict;
use Data::Dumper;
my @AoA = (['a,','b','c'],
['a',',b','c'],
);
my $uAoA = uniqAoA(\@AoA);
print Dumper $uAoA;
sub uniqAoA
{
my ($array) = @_;
my %res;
foreach my $ar (@{$array})
{
my $str = join (",",@{$ar});
$res{$str} = 1;
}
my @kys = keys(%res);
my @final;
foreach(@kys)
{
my @a = split(",",$_);
push @final, [ @a ];
}
return \@final;
}
This should return the orginal AoA, but it returns:
$VAR1 = [
[
'a',
'',
'b',
'c'
]
];
Problems are:
- It thought the two level 2 arrays are the same, when they are different.
- The element contained in the returned array was not originally there.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
my @AoA = (['a,','b','c'],
['a',',b','c'],
);
Your example above is problematic:
Shouldn't it be this:
my @AoA = (['a','b','c'],
['a','b','c'],
);
With the latest modification of your example
my code still return the correct answer.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Re: Fast Way to Return Unique Array of Array
by TedPride (Priest) on Sep 05, 2005 at 06:21 UTC
|
Just set $delim to whatever character or sequence of characters is not going to be found inside any of your nested array fields. I used tabs.
use strict;
use warnings;
use Data::Dumper;
my @AoA = (['a','b','c'],
['a','b','c'],
['a','b','d'],
['a','b','d']);
my (%h, @uAoA);
for (@AoA) { push @uAoA, $_ if !$h{join $;, @$_}++; }
print Dumper \@uAoA;
EDIT: Edited code to use $;, as per suggestion.
| [reply] [Watch: Dir/Any] [d/l] |
|
nice solution, but better to use $; instead of $delim IMHO.... see perlvar manpage.
| [reply] [Watch: Dir/Any] |
Re: Fast Way to Return Unique Array of Array
by pg (Canon) on Sep 05, 2005 at 07:16 UTC
|
Slightly modify the original uniq(), it works:
use Data::Dumper;
my @AoA = (['a','b','c'],
['a','b','c'],
['a','b','d'],
['a','b','d']);
my @uAoA = uniq(@AoA);
print Dumper \@uAoA;
sub uniq () {
my %h;
map { $h{join($;, @$_)}++ == 0 ? $_ : () } @_;
}
| [reply] [Watch: Dir/Any] [d/l] |
|
map { $h{join($;, @$_)}++ == 0 ? $_ : () } @_;
becomes
grep { ! $h{join($;, @$_)}++ } @_;
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Could one replace join($;, @$_) with Dumper($_) (or some variant thereof), to allow for multiple levels of data structure?
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
|