166

我在 Perl 中有一个数组:

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

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

4

11 回答 11

173

你可以像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

于 2008-08-11T10:16:22.770 回答
125

Perl 文档附带了一组很好的常见问题解答。您的问题经常被问到:

% perldoc -q duplicate

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


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

如何从列表或数组中删除重复元素? (由布赖恩·d·福伊提供)

使用哈希。当您想到“唯一”或“重复”这些词时,请考虑“哈希键”。

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

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

   my @unique = keys %hash;

如果您想使用模块,请尝试“List::MoreUtils”中的“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 中没有键。“next”语句创建键并立即使用它的值,即“undef”,因此循环继续“push”并增加该键的值。下一次循环看到同一个元素时,它的键存在于哈希中,并且该键的值为真(因为它不是 0 或“undef”),所以下一次跳过该迭代并且循环转到下一个元素。

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

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

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

   my %seen = ();
   my @unique = grep { ! $seen{ $_ }++ } @array;
于 2008-08-11T14:27:46.150 回答
70

从 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);
于 2008-08-31T10:01:18.683 回答
24

我通常的做法是:

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

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

于 2008-08-11T10:18:45.090 回答
10

可以用一个简单的 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

于 2011-11-09T21:23:25.667 回答
9

方法一:使用哈希

逻辑:散列只能有唯一的键,因此遍历数组,为数组的每个元素分配任何值,保持元素作为该散列的键。哈希的返回键,它是你唯一的数组。

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

方法 2:扩展方法 1 以实现可重用性

如果我们应该在代码中多次使用此功能,最好创建一个子程序。

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

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

use List::MoreUtils qw(uniq);
my @unique = uniq(@array);
于 2017-05-09T15:29:44.307 回答
8

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

%seen=();
@unique = grep { ! $seen{$_} ++ } @array;
于 2010-10-23T16:18:36.777 回答
4

最后一张还不错 我只是稍微调整一下:

my @arr;
my @uniqarr;

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

我认为这可能是最易读的方式。

于 2009-01-23T23:35:59.633 回答
1

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

但是,我建议对那些关心重复计数但关心顺序的人进行修改。

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
于 2019-01-02T00:38:37.720 回答
0

试试这个,似乎 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";
于 2015-05-26T01:56:44.987 回答
0

使用唯一哈希键的概念:

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

输出:acbd

于 2017-03-30T09:47:16.577 回答