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

145

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

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

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

Теги:
arrays
duplicates
unique

10 ответов

151
Лучший ответ

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

  • 27
    пожалуйста, не используйте $ a или $ b в примерах, так как они являются волшебными глобальными переменными sort ()
  • 2
    Это my лексика в этой области, так что все в порядке. При этом, возможно, можно было бы выбрать более описательное имя переменной.
Показать ещё 5 комментариев
109

Документация 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 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 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;
  • 0
    perldoc.perl.org/...
  • 16
    Джон из Ма-Анзерс ворует Ма-Реп!
Показать ещё 2 комментария
66

Установите 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);
  • 2
    Это ответ! Но я могу проголосовать только один раз.
  • 4
    Тот факт, что List :: MoreUtils не связан с Perl своего рода, повреждает переносимость проектов, использующих его :( (я, например, не буду)
Показать ещё 2 комментария
21

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

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

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

  • 2
    У этого есть недостаток - не сохранять первоначальный заказ, если он вам нужен.
  • 0
    Лучше использовать кусочки вместо цикла foreach : @unique{@myarray}=()
6

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

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

Может быть сделано с простым 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 создает анонимный хэш. Ключи извлекаются из хэша и подаются на @out

3

Это последнее было очень хорошо. Я бы немного изменил его:

my @arr;
my @uniqarr;

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

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

  • 1
    Более независимый ..
0

Способ 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);
0

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню