Як видалити повторювані елементи з масиву в Perl?


Відповіді:


168

Ви можете зробити щось подібне, як показано в 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


28
будь ласка, не використовуйте $ a або $ b у прикладах, оскільки вони є чарівними глобалами сорту ()
szabgab

2
Це myлексика в цьому обсязі, тому це добре. Однак, можливо, можна вибрати більш описову назву змінної.
ефеміент

2
@ephemient так, але якби ви додали сортування в цю функцію, це призведе до козиру, $::aі $::bчи не так?
vol7ron

5
@BrianVandenberg Ласкаво просимо у світ 1987 року - коли це було створено - і майже 100% сумісність зворотних слів для perl - тому його неможливо усунути.
szabgab

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

120

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

% perldoc -q duplicate

Відповідь, скопійована та вставлена ​​з результату команди вище, відображається нижче:

Знайдено в /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
 Як я можу видалити повторювані елементи зі списку чи масиву?
   (сприяв Брайан d foy)

   Використовуйте хеш. Коли ви думаєте, що слова "унікальні" або "дублюються", подумайте
   "хеш-ключі".

   Якщо вам не байдуже порядок елементів, ви могли просто
   створити хеш, потім витягнути ключі. Не важливо, як ти
   Створіть цей хеш: просто ви використовуєте "клавіші", щоб отримати унікальні елементи.

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

       мій @unique = ключі% хеш;

   Якщо ви хочете використовувати модуль, спробуйте функцію "uniq" від
   "Список :: MoreUtils". У контексті списку він повертає унікальні елементи,
   збереження їх порядку в списку. У скалярному контексті він повертає
   кількість унікальних елементів.

       використовувати Список :: MoreUtils qw (uniq);

       мій @unique = uniq (1, 2, 3, 4, 4, 5, 6, 5, 7); # 1,2,3,4,5,6,7
       мій $ унікальний = uniq (1, 2, 3, 4, 4, 5, 6, 5, 7); №7

   Ви також можете пройти кожен елемент і пропустити ті, які ви бачили
   раніше. Використовуйте хеш для відстеження. Перший раз, коли цикл бачить
   елемент, у цього елемента немає ключа в% Seen. "Наступний" заява створює
   ключ і негайно використовує його значення, яке є "undef", тому цикл
   продовжує "натискати" і збільшує значення для цієї клавіші. Наступний
   Коли цикл бачить той самий елемент, його ключ існує в хеші та
   значення для цього ключа є істинним (оскільки це не 0 або "undef"), тож
   Наступний пропускає цю ітерацію і цикл переходить до наступного елемента.

       мій @unique = ();
       мій% бачив = ();

       передбачити мій $ elem (@array)
       {
         наступний, якщо $ saw {$ elem} ++;
         push @unique, $ elem;
       }

   Ви можете написати це коротше, використовуючи греп, який робить те саме
   річ.

       мій% бачив = ();
       мій @unique = grep {! $ saw {$ _} ++} @array;


17
Джон Із у ма-анжерах краде мах реп!
Брайан d foy

5
Я думаю, ви повинні отримати бонусні бали за те, що насправді розглядаєте питання.
Бред Гілберт

2
Мені подобається, що найкраща відповідь - це 95% копію-вставлення та 3 пропозиції OC. Щоб бути абсолютно ясно, це є найкращою відповіддю; Я просто вважаю цей факт кумедним.
Парфянський розстріл

70

Список встановлення :: 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);

4
Той факт, що List :: MoreUtils не в комплекті w / perl свого роду, шкодить переносимості проектів з його використанням :( (я для одного не буду)
yPhil

3
@Ranguard: @dup_listмає бути всередині uniqдзвінка, не@dups
incutonez

@yassinphilip CPAN - це одна з речей, яка робить Perl настільки потужним і великим, як це може бути. Якщо ви пишете свої проекти, засновані лише на основних модулях, ви ставите величезний ліміт свого коду, а також, можливо, письмово написаний код, який намагається зробити те, що деякі модулі роблять набагато краще, щоб уникнути їх використання. Крім того, використання основних модулів нічого не гарантує, оскільки різні версії Perl можуть додавати або видаляти основні модулі з дистрибутиву, тому мобільність все ще залежить від цього.
Франсіско Зарабобозо

24

Мій звичайний спосіб зробити це:

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

Якщо ви використовуєте хеш, додайте елементи до хешу. Ви також маєте бонус знати, скільки разів кожен елемент з’являється у списку.


2
Це має і мінус - не збереження початкового замовлення, якщо воно вам потрібно.
Натан Фелман

Краще використовувати шматочки замість foreachпетлі:@unique{@myarray}=()
Onlyjob

8

Змінна @array - це список з повторюваними елементами

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

7

Це можна зробити простим вкладишем Perl one.

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


4

Цей останній був досить гарний. Я просто трохи підправити це:

my @arr;
my @uniqarr;

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

Я думаю, що це, мабуть, найбільш читабельний спосіб це зробити.


4

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

1

Попередні відповіді в значній мірі узагальнюють можливі шляхи виконання цього завдання.

Тим НЕ менше, я пропоную модифікацію для тих , хто НЕ піклується про підрахунку дублікатів, але зробити турботу про порядок.

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; } Акуратний.
stevesliva

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

0

Використання концепції унікальних хеш-ключів:

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

Вихід: acbd

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.