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

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

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

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
159
0
220 733
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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

пожалуйста, не используйте $ a или $ b в примерах, так как они являются волшебными глобальными переменными sort ()

szabgab 17.09.2008 11:50

В этом контексте это лексика my, так что все в порядке. При этом, возможно, можно было бы выбрать более описательное имя переменной.

ephemient 18.01.2010 20:51

@ephemient: да, но если бы вы добавили сортировку в эту функцию, она бы превзошла $::a и $::b, не так ли?

vol7ron 21.02.2012 20:45

@szabgab, если это так, это невероятно плохое дизайнерское решение для sort использовать нелокальные переменные.

Brian Vandenberg 15.06.2012 01:12

@BrianVandenberg Добро пожаловать в мир 1987 года - когда он был создан - и почти стопроцентная обратная совместимость для perl - так что от него невозможно избавиться.

szabgab 25.06.2012 12:19

sub uniq { my %seen; grep !$seen{$_}++, @_ } - лучшая реализация, так как она поддерживает порядок без каких-либо затрат. Или, что еще лучше, воспользуйтесь одним из List :: MoreUtils.

ikegami 06.11.2012 22:51

@ vol7tron означает "обратная совместимость", извините, это меня беспокоило ;-)

Tyler 29.08.2015 08:17

Perl v5.26.0 и более поздние версии, List::Util имеет uniq, поэтому MoreUtils не потребуется

Sundeep 30.10.2020 11:33

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

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

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

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

Nathan Fellman 18.02.2014 16:34

Лучше использовать ломтики вместо цикла foreach: @unique{@myarray}=()

Onlyjob 20.09.2015 18:46

Документация 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;
perldoc.perl.org/…
szabgab 17.09.2008 11:48

Джон iz в mah anzers крадет mah rep!

brian d foy 10.10.2008 03:41

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

Brad Gilbert 24.10.2008 19:14

Мне нравится, что лучший ответ - 95% copy-paste и 3 предложения OC. Чтобы быть совершенно ясным, это является лучший ответ; Я просто нахожу этот факт забавным.

Parthian Shot 21.07.2014 22:23

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

Тот факт, что List :: MoreUtils не связан с perl, как бы повреждает переносимость проектов, использующих его :( (я, например, не буду)

yPhil 19.03.2012 06:00

@Ranguard: @dup_list должен быть внутри вызова uniq, а не @dups

incutonez 11.11.2013 18:48

@yassinphilip CPAN - одна из тех вещей, которые делают Perl настолько мощным и замечательным, насколько это возможно. Если вы пишете свои проекты, основанные только на основных модулях, вы накладываете огромное ограничение на свой код вместе с, возможно, полностью написанным кодом, который пытается делать то, что некоторые модули делают намного лучше, просто чтобы избежать их использования. Кроме того, использование основных модулей ничего не гарантирует, поскольку разные версии Perl могут добавлять или удалять основные модули из дистрибутива, поэтому переносимость все еще зависит от этого.

Francisco Zarabozo 27.06.2017 17:38

Perl v5.26.0 и более поздние версии, List::Util имеет uniq, поэтому MoreUtils не потребуется

Sundeep 30.10.2020 11:32

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

my @arr;
my @uniqarr;

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

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

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

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

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

Попробуйте это, похоже, для правильной работы функции 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 @array  = ("a","b","c","b","a","d","c","a","d");
my %hash   = map { $_ => 1 } @array;
my @unique = keys %hash;
print "@unique","\n";

Выход: а в б г

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

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

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

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

Для сравнения: sub uniq{ my %seen; undef @seen{@_}; keys %seen; } Neat.

stevesliva 09.05.2019 22:44

Другие вопросы по теме