Как удалить повторяющиеся элементы из массива в Perl?

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

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

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

Ответов (11)

Решение

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

Установить List :: 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);

Можно сделать с помощью простого 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

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

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

Выход: acbd

Метод 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);

Попробуйте это, похоже, для правильной работы функции 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 @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

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

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

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

Документация 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 = keys% 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", поэтому цикл
   продолжает «толкать» и увеличивает значение для этого ключа. Следующий
   когда цикл видит тот же элемент, его ключ существует в хэше и
   значение для этого ключа истинно (поскольку оно не 0 или "undef"), поэтому
   next пропускает эту итерацию, и цикл переходит к следующему элементу.

       мой @unique = ();
       мой% видел = ();

       foreach мой $ elem (@array)
       {
         следующий, если $ видел {$ elem} ++;
         push @unique, $ elem;
       }

   Вы можете написать это более коротко, используя команду grep, которая делает то же самое
   вещь.

       мой% видел = ();
       мой @unique = grep {! $ замечен {$ _} ++} @array;

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

my @arr;
my @uniqarr;

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

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

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

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