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


156

我在Perl中有一个数组:

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

如何从阵列中删除重复项?

Answers:


168

您可以按照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


28
请不要在示例中使用$ a或$ b,因为它们是sort()的神奇全局变量
szabgab

2
my在这个范围内这是一个词汇,所以很好。话虽如此,可能会选择一个更具描述性的变量名。
短暂

2
@ephemient是的,但是如果您要在此函数中添加排序,那么它将胜过$::aand $::b,不是吗?
vol7ron 2012年

5
@BrianVandenberg欢迎来到1987年的世界-创建之初-几乎有100%的backl兼容Perl的能力-因此无法消除它。
szabgab 2012年

18
sub uniq { my %seen; grep !$seen{$_}++, @_ }这是一个更好的实现,因为它可以免费保留订单。甚至更好的是,使用List :: MoreUtils中的一个。
ikegami 2012年

120

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

% perldoc -q duplicate

从上面命令的输出中回答,复制和粘贴如下所示:

在/usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod中找到
 如何从列表或数组中删除重复的元素?
   (由brian d foy贡献)

   使用哈希。当您认为“独特”或“重复”一词时,请考虑
   “哈希键”。

   如果您不在乎元素的顺序,则可以
   创建哈希,然后提取密钥。你怎么不重要
   创建该哈希值:仅使用“键”来获取唯一元素。

       我的%hash = map {$ _,1} @array;
       #或哈希切片:@hash {@array} =();
       #或foreach:$ hash {$ _} = 1 foreach(@array);

       我的@unique =键%hash;

   如果要使用模块,请尝试使用“ uniq”功能
   “列表:: MoreUtils”。在列表上下文中,它返回唯一元素,
   将其顺序保留在列表中。在标量上下文中,它返回
   独特元素的数量。

       使用List :: MoreUtils qw(uniq);

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

   您还可以浏览每个元素,并跳过已看到的元素
   之前。使用哈希来跟踪。循环第一次看到
   元素,该元素在%Seen中没有键。“下一个”语句创建
   键并立即使用其值,即“ undef”,因此循环
   继续到“推”并增加该键的值。下一个
   当循环看到相同的元素时,其键存在于哈希中
   该键的值是true(因为它不是0或“ undef”),所以
   next跳过该迭代,循环转到下一个元素。

       我的@unique =();
       我的%seen =();

       foreach我的$ elem(@array)
       {
         接下来,如果$ seen {$ elem} ++;
         推@unique,$ elem;
       }

   您可以使用grep进行更简短的编写,其功能相同
   事情。

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


17
约翰·伊兹(John iz)在mah anzers中偷走mah rep!
brian d foy

5
我认为您应该通过实际查找问题来获得奖励积分。
布拉德·吉尔伯特

2
我喜欢最好的答案是95%复制粘贴和3句OC。确切地说,这最好的答案。我只是觉得这个事实很有趣。
Parthian Shot

70

安装列表::来自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);

4
List :: MoreUtils没有与perl捆绑在一起的事实破坏了使用它的项目的可移植性:(((我不会)
yPhil 2012年

3
@Ranguard:@dup_list应该在uniq通话中,而不是@dups
incutonez

@yassinphilip CPAN是使Perl尽可能强大和强大的功能之一。如果您仅基于核心模块编写项目,那么您的代码以及可能编写得很少的代码都将极大地限制代码,这些代码试图做一些更好的模块,只是避免使用它们。而且,使用核心模块不能保证任何事情,因为不同的Perl版本可以在发行版中添加或删除核心模块,因此可移植性仍然取决于此。
Francisco Zarabozo

24

我通常的做法是:

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

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


2
如果需要,这样做的缺点是不保留原始顺序。
弥敦道·费尔曼

最好使用切片而不是foreach循环:@unique{@myarray}=()
Onlyjob

8

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

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

7

可以使用简单的Perl one衬板完成。

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建立一个匿名哈希。从哈希中提取密钥并将其输入@out


4

最后一个很好。我会稍微调整一下:

my @arr;
my @uniqarr;

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

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


4

方法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);

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{$_}true 时发生短路,一旦“关闭%record”,就会听到听到的声音。

您还可以选择这种可笑性,它利用了自动生存和哈希键的存在:

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

但是,这可能会导致一些混乱。

如果您不关心顺序或重复计数,则可以使用哈希片和我刚才提到的技巧进行另一次黑客入侵:

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

对于那些比较:sub uniq{ my %seen; undef @seen{@_}; keys %seen; } 整洁。
stevesliva,

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";

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.