如何在 Perl 中从数组中删除重复项?

我在 Perl 中有一个数组:

my @my_array = ("one","two","three","two","three");

如何从数组中删除副本?

234995 次浏览

你可以像 Perlfaq4中演示的那样:

sub uniq {
my %seen;
grep !$seen{$_}++, @_;
}


my @array = qw(one two three two three);
my @filtered = uniq(@array);


print "@filtered\n";

产出:

one two three

如果要使用模块,请尝试 List::MoreUtils中的 uniq函数

我通常的做法是:

my %unique = ();
foreach my $item (@myarray)
{
$unique{$item} ++;
}
my @myuniquearray = keys %unique;

如果使用哈希并将项添加到哈希。你还可以知道每个项目在列表中出现的次数。

Perl 文档提供了一系列不错的 FAQ:

% perldoc -q duplicate

从上面的命令输出中复制并粘贴的答案如下:


在/usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod 中找到

如何从列表或数组中删除重复元素? (Brian d Foy 提供)

使用散列表。当你想到“独一无二”或“复制”这些词时,想一想 “哈希键”。

如果您不关心元素的顺序,您可以只创建散列,然后提取键。如何创建散列并不重要: 重要的是使用“ key”来获取唯一的元素。

   my %hash   = map { $_, 1 } @array;
# or a hash slice: @hash{ @array } = ();
# or a foreach: $hash{$_} = 1 foreach ( @array );


my @unique = keys %hash;

如果要使用模块,请尝试从 “ List: : MoreUtils”。在列表上下文中,它返回唯一的元素,保持它们在列表中的顺序。在标量上下文中,它返回唯一元素的数量。

   use List::MoreUtils qw(uniq);


my @unique = uniq( 1, 2, 3, 4, 4, 5, 6, 5, 7 ); # 1,2,3,4,5,6,7
my $unique = uniq( 1, 2, 3, 4, 4, 5, 6, 5, 7 ); # 7

您还可以遍历每个元素,跳过您已经看到的元素 使用散列来跟踪。循环第一次看到 元素,该元素在% Seen 中没有键 并立即使用它的值,即“ undef”,因此循环 继续“推”并增加该键的值 当循环看到同一个元素时,它的键存在于散列和 该键的值为 true (因为它不是0或“ undef”) ,所以 Next 跳过该迭代,循环转到下一个元素。

   my @unique = ();
my %seen   = ();


foreach my $elem ( @array )
{
next if $seen{ $elem }++;
push @unique, $elem;
}

您可以使用 grep 编写更简短的代码,它执行同样的操作。

   my %seen = ();
my @unique = grep { ! $seen{ $_ }++ } @array;

从 CPAN 安装 列表: : MoreUtils

然后在你的代码中:

use strict;
use warnings;
use List::MoreUtils qw(uniq);


my @dup_list = qw(1 1 1 2 3 4 4);


my @uniq_list = uniq(@dup_list);

最后一个很不错,我会稍微调整一下:

my @arr;
my @uniqarr;


foreach my $var ( @arr ){
if ( ! grep( /$var/, @uniqarr ) ){
push( @uniqarr, $var );
}
}

我认为这可能是最可读的方式来做到这一点。

变量 @array是具有重复元素的列表

%seen=();
@unique = grep { ! $seen{$_} ++ } @array;

可以用一个简单的 Perl 一行程序来完成。

my @in=qw(1 3 4  6 2 4  3 2 6  3 2 3 4 4 3 2 5 5 32 3); #Sample data
my @out=keys %\{\{ map{$_=>1}@in}}; # Perform PFM
print join ' ', sort{$a<=>$b} @out;# Print data back out sorted and in order.

PFM 块执行以下操作:

@in中的数据被输入到 map中。map构建一个匿名散列。从散列中提取 keys并提要到 @out

试试这个,看起来 uniq 函数需要一个排序列表才能正常工作。

use strict;


# Helper function to remove duplicates in a list.
sub uniq {
my %seen;
grep !$seen{$_}++, @_;
}


my @teststrings = ("one", "two", "three", "one");


my @filtered = uniq @teststrings;
print "uniq: @filtered\n";
my @sorted = sort @teststrings;
print "sort: @sorted\n";
my @sortedfiltered = uniq sort @teststrings;
print "uniq sort : @sortedfiltered\n";

使用唯一哈希键的概念:

my @array  = ("a","b","c","b","a","d","c","a","d");
my %hash   = map { $_ => 1 } @array;
my @unique = keys %hash;
print "@unique","\n";

产出: 一个联邦调查局的人

方法1: 使用散列

逻辑: 哈希只能有唯一的键,所以迭代数组,为数组的每个元素赋任何值,保持元素作为哈希的键。返回散列的键,它是您唯一的数组。

my @unique = keys {map {$_ => 1} @array};

方法2: 方法1的可重用性扩展

如果我们要在代码中多次使用这个功能,最好制作一个子例程。

sub get_unique {
my %seen;
grep !$seen{$_}++, @_;
}
my @unique = get_unique(@array);

方法3: 使用模块 List::MoreUtils

use List::MoreUtils qw(uniq);
my @unique = uniq(@array);

以前的答案几乎总结了完成这项任务的可能方法。

但是,我建议修改那些谁的 不要关心 计数的重复,但 关心秩序。

my @record = qw( yeah I mean uh right right uh yeah so well right I maybe );
my %record;
print grep !$record{$_} && ++$record{$_}, @record;

请注意,先前建议的 grep !$seen{$_}++ ...在负值之前递增 $seen{$_},因此无论它是否已经是 %seen,增量都会发生。然而,当 $record{$_}是真实的时候,上面的短路,留下曾经听到的“关闭 %record”。

你也可以尝试这种荒谬的做法,它利用了自动化和哈希键的存在:

...
grep !(exists $record{$_} || undef $record{$_}), @record;

然而,这可能会导致一些混乱。

如果你既不关心顺序也不关心重复计数,那么你可以使用散列片和我刚才提到的技巧:

...
undef @record{@record};
keys %record; # your record, now probably scrambled but at least deduped