Как удалить повторяющиеся элементы из массива в Perl? - PullRequest
147 голосов
/ 11 августа 2008

У меня есть массив в Perl:

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

Как удалить дубликаты из массива?

Ответы [ 11 ]

155 голосов
/ 11 августа 2008

Вы можете сделать что-то подобное, как показано в 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

118 голосов
/ 11 августа 2008

Документация Perl поставляется с хорошей коллекцией часто задаваемых вопросов. Ваш вопрос часто задают:

% perldoc -q duplicate

Ответ, скопированный и вставленный из вывода команды, приведенной выше, отображается ниже:

Found in /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
 How can I remove duplicate elements from a list or array?
   (contributed by brian d foy)

   Use a hash. When you think the words "unique" or "duplicated", think
   "hash keys".

   If you don't care about the order of the elements, you could just
   create the hash then extract the keys. It's not important how you
   create that hash: just that you use "keys" to get the unique elements.

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

       my @unique = keys %hash;

   If you want to use a module, try the "uniq" function from
   "List::MoreUtils". In list context it returns the unique elements,
   preserving their order in the list. In scalar context, it returns the
   number of unique elements.

       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

   You can also go through each element and skip the ones you've seen
   before. Use a hash to keep track. The first time the loop sees an
   element, that element has no key in %Seen. The "next" statement creates
   the key and immediately uses its value, which is "undef", so the loop
   continues to the "push" and increments the value for that key. The next
   time the loop sees that same element, its key exists in the hash and
   the value for that key is true (since it's not 0 or "undef"), so the
   next skips that iteration and the loop goes to the next element.

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

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

   You can write this more briefly using a grep, which does the same
   thing.

       my %seen = ();
       my @unique = grep { ! $seen{ $_ }++ } @array;
66 голосов
/ 31 августа 2008

Установить Список :: MoreUtils из CPAN

Тогда в вашем коде:

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);
22 голосов
/ 11 августа 2008

Мой обычный способ сделать это:

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

Если вы используете хеш и добавляете элементы в хеш. У вас также есть бонус за знание того, сколько раз каждый элемент появляется в списке.

7 голосов
/ 10 ноября 2011

Можно сделать с помощью простого Perl one liner.

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

7 голосов
/ 23 октября 2010

Переменная @array - это список с дублирующимися элементами

%seen=();
@unique = grep { ! $seen{$_} ++ } @array;
4 голосов
/ 09 мая 2017

Способ 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);
4 голосов
/ 24 января 2009

Последний был довольно хорош. Я бы просто немного подправил:

my @arr;
my @uniqarr;

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

Я думаю, что это, вероятно, самый читаемый способ сделать это.

1 голос
/ 02 января 2019

Предыдущие ответы в значительной степени суммируют возможные пути выполнения этой задачи.

Тем не менее, я предлагаю модификацию для тех, кто не заботится о подсчитывает дубликаты, но делает заботится о порядке.

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
0 голосов
/ 30 марта 2017

Использование концепции уникальных хеш-ключей:

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

...