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

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

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

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

11.08.2008 10:04:32
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

164
21.06.2014 01:39:29
пожалуйста, не используйте $ a или $ b в примерах, так как они являются волшебными глобальными переменными sort ()
szabgab 17.09.2008 07:50:06
Это myлексическое в этом смысле, так что все в порядке. При этом, возможно, можно было бы выбрать более описательное имя переменной.
ephemient 18.01.2010 17:51:13
@ephemient да, но если бы вы добавили сортировку в эту функцию, то это было бы лучше, $::aи $::bне так ли?
vol7ron 21.02.2012 16:45:38
@BrianVandenberg Добро пожаловать в мир 1987 года - когда он был создан - и почти 100% -ную совместимость с обратным словом для perl - так что его нельзя устранить.
szabgab 25.06.2012 08:19:32
sub uniq { my %seen; grep !$seen{$_}++, @_ }это лучшая реализация, поскольку она сохраняет порядок без затрат. Или даже лучше, используйте тот из List :: MoreUtils.
ikegami 6.11.2012 18:51:50

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

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

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

23
20.07.2014 16:35:58
У этого есть недостаток - не сохранять первоначальный заказ, если он вам нужен.
Nathan Fellman 18.02.2014 12:34:49
Лучше использовать ломтики вместо foreachцикла:@unique{@myarray}=()
Onlyjob 20.09.2015 15:46:45

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

% perldoc -q duplicate

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

Найдено в /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
 Как удалить повторяющиеся элементы из списка или массива?
   (предоставлено Брайаном Д. Фой)

   Используйте хэш. Когда вы думаете, слова «уникальный» или «дублированный», подумайте
   "ключи хеша".

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

       мой% hash = map {$ _, 1} @array;
       # или фрагмент хеша: @hash {@array} = ();
       # или foreach: $ hash {$ _} = 1 foreach (@array);

       мой @unique = keys% hash;

   Если вы хотите использовать модуль, попробуйте функцию "uniq" из
   "Список :: MoreUtils". В контексте списка он возвращает уникальные элементы,
   сохраняя их порядок в списке. В скалярном контексте он возвращает
   количество уникальных элементов.

       использовать 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

   Вы также можете пройти через каждый элемент и пропустить те, которые вы видели
   перед. Используйте хеш для отслеживания. Первый раз, когда цикл видит
   элемент, этот элемент не имеет ключа в% Seen. «Следующее» утверждение создает
   ключ и сразу же использует его значение, которое является "undef", поэтому цикл
   продолжает нажимать и увеличивает значение для этой клавиши. Следующий
   Когда цикл видит тот же элемент, его ключ существует в хеше и
   значение для этого ключа является истинным (так как это не 0 или "undef"), поэтому
   next пропускает эту итерацию, и цикл переходит к следующему элементу.

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

       foreach мой $ elem (@array)
       {
         далее, если $ seen {$ elem} ++;
         push @unique, $ elem;
       }

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

       мой% видел = ();
       мой @unique = grep {! $ seen {$ _} ++} @array;
120
11.08.2008 14:30:52
szabgab 17.09.2008 07:48:09
Джон из Ма-Анзерс ворует Ма-Реп!
brian d foy 9.10.2008 23:41:59
Я думаю, что вы должны получить бонусные баллы за то, что действительно изучили вопрос.
Brad Gilbert 24.10.2008 15:14:02
Мне нравится, что лучший ответ - 95% копирование-вставка и 3 предложения OC. Чтобы быть совершенно ясно, это является лучшим ответом; Я просто нахожу этот факт забавным.
Parthian Shot 21.07.2014 18:23:45

Список установки :: 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);
68
15.05.2016 14:32:32
Тот факт, что List :: MoreUtils не входит в пакет, вроде как повреждает переносимость проектов, использующих его :( (я, например, не буду)
yPhil 19.03.2012 02:00:40
@Ranguard: @dup_listдолжен быть внутри uniqзвонка, а не@dups
incutonez 11.11.2013 14:48:53
@yassinphilip CPAN - это то, что делает Perl настолько мощным и мощным, насколько это возможно. Если вы пишете свои проекты, основанные только на базовых модулях, вы накладываете огромный предел на свой код, а также, возможно, на плохо написанный код, который пытается делать то, что некоторые модули делают намного лучше, просто избегая их использования. Кроме того, использование основных модулей ничего не гарантирует, поскольку различные версии Perl могут добавлять или удалять основные модули из дистрибутива, поэтому переносимость все еще зависит от этого.
Francisco Zarabozo 27.06.2017 14:38:43

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

my @arr;
my @uniqarr;

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

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

4
16.07.2013 03:37:30

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

%seen=();
@unique = grep { ! $seen{$_} ++ } @array;
8
15.07.2013 19:02:12

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

7
9.11.2011 21:30:21

Попробуйте, кажется, что для правильной работы функции 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
26.05.2015 01:56:44

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

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

Выход: acbd

0
30.03.2017 09:47:16

Способ 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
9.05.2017 15:29:44

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

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

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
1
2.01.2019 02:32:07
Для тех, кто сравнивает: sub uniq{ my %seen; undef @seen{@_}; keys %seen; } аккуратно.
stevesliva 9.05.2019 19:44:30