我有一个 Perl 数组:
my @my_array = ("one","two","three","two","three");
如何从数组中删除重复项?
您可以执行类似的操作,如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
如果您想使用模块,请尝试
uniq
中的
List::MoreUtils
函数,或者从 v5.26.0 开始,使用 核心模块 List::Util。两者都将 undef
视为单独的值,而不发出警告(如上面的草图所示)。
Perl 文档附带了一系列很好的常见问题解答。您的问题经常被问到:
% perldoc -q duplicate
从上面命令的输出中复制并粘贴的答案如下所示:
如何从列表或数组中删除重复元素? (由布莱恩·福伊贡献)
使用哈希。当您想到“独特”或“重复”这些词时,请想一想 “哈希键”。
如果您不关心元素的顺序,您可以只创建哈希然后提取键。如何创建该哈希并不重要:只要使用“键”来获取唯一元素即可。
my %hash = map { $_, 1 } @array;
# or a hash slice: @hash{ @array } = ();
# or a foreach: $hash{$_} = 1 foreach ( @array );
my @unique = keys %hash;
如果您想使用模块,请尝试使用“uniq”功能 “列表::更多实用工具”。在列表上下文中,它返回唯一元素,并保留它们在列表中的顺序。在标量上下文中,它返回唯一元素的数量。
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 安装 List::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 %unique = ();
foreach my $item (@myarray)
{
$unique{$item} ++;
}
my @myuniquearray = keys %unique;
如果您使用哈希并将项目添加到哈希中。您还可以了解每个项目在列表中出现的次数。
可以使用简单的 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
逻辑:哈希只能有唯一的键,因此迭代数组,为数组的每个元素分配任意值,并将元素保留为该哈希的键。返回散列的键,它是您唯一的数组。
my @unique = keys {map {$_ => 1} @array};
如果我们要在代码中多次使用此功能,最好创建一个子例程。
sub get_unique {
my %seen;
grep !$seen{$_}++, @_;
}
my @unique = get_unique(@array);
List::MoreUtils
use List::MoreUtils qw(uniq);
my @unique = uniq(@array);
变量
@array
是具有重复元素的列表
%seen=();
@unique = grep { ! $seen{$_} ++ } @array;
最后一张非常好。我只是稍微调整一下:
my @arr;
my @uniqarr;
foreach my $var ( @arr ){
if ( ! grep( /$var/, @uniqarr ) ){
push( @uniqarr, $var );
}
}
我认为这可能是最易读的方法。
之前的答案几乎总结了完成此任务的可能方法。
但是,我建议对那些不关心计数重复项,但确实关心顺序的人进行修改。
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
试试这个,似乎 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";
输出: a c b d