Jak usunąć zduplikowane elementy z tablicy w Perlu?

156

Mam tablicę w Perlu:

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

Jak usunąć duplikaty z tablicy?

David
źródło

Odpowiedzi:

168

Możesz zrobić coś takiego, jak pokazano w perlfaq4 :

sub uniq {
    my %seen;
    grep !$seen{$_}++, @_;
}

my @array = qw(one two three two three);
my @filtered = uniq(@array);

print "@filtered\n";

Wyjścia:

one two three

Jeśli chcesz użyć modułu, wypróbuj uniqfunkcję zList::MoreUtils

Greg Hewgill
źródło
28
proszę nie używać $ a ani $ b w przykładach, ponieważ są to magiczne
globale
2
W mytym zakresie jest leksykalny, więc jest w porządku. Biorąc to pod uwagę, prawdopodobnie można wybrać bardziej opisową nazwę zmiennej.
ephemient
2
@ephemient tak, ale gdybyś dodał sortowanie w tej funkcji, miałoby to atut $::ai $::b, prawda?
vol7ron
5
@BrianVandenberg Witamy w świecie 1987 roku - kiedy ten został stworzony - i prawie 100% kompatybilności zwrotów z perlem - więc nie można tego wyeliminować.
szabgab
18
sub uniq { my %seen; grep !$seen{$_}++, @_ }jest lepszą implementacją, ponieważ zachowuje porządek bez żadnych kosztów. Lub jeszcze lepiej, użyj tego z List :: MoreUtils.
ikegami
120

Dokumentacja Perla zawiera niezłą kolekcję często zadawanych pytań. Twoje pytanie jest często zadawane:

% perldoc -q duplicate

Odpowiedź, skopiuj i wklej z wyniku powyższego polecenia, pojawia się poniżej:

Znaleziono w /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
 Jak mogę usunąć zduplikowane elementy z listy lub tablicy?
   (nadesłał brian d foy)

   Użyj skrótu. Kiedy myślisz, że słowa „niepowtarzalny” lub „zduplikowany”, pomyśl
   „klucze skrótu”.

   Jeśli nie zależy Ci na kolejności elementów, możesz po prostu
   utwórz skrót, a następnie wyodrębnij klucze. Nie jest ważne, jak ty
   stwórz ten hash: wystarczy, że używasz „kluczy”, aby uzyskać unikalne elementy.

       mój% hash = map {$ _, 1} @array;
       # lub krzyżyk: @hash {@array} = ();
       # lub foreach: $ hash {$ _} = 1 foreach (@array);

       my @unique = klucze% hash;

   Jeśli chcesz użyć modułu, wypróbuj funkcję „uniq” z
   „List :: MoreUtils”. W kontekście listowym zwraca unikalne elementy,
   zachowanie ich kolejności na liście. W kontekście skalarnym zwraca
   liczba unikalnych elementów.

       użyj 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

   Możesz także przejść przez każdy element i pominąć te, które widziałeś
   przed. Użyj skrótu, aby śledzić. Za pierwszym razem, gdy pętla napotka plik
   element, ten element nie ma klucza w% Seen. Tworzy instrukcja „next”
   klucz i natychmiast używa jego wartości, czyli „undef”, czyli pętli
   kontynuuje do „wypychania” i zwiększa wartość tego klucza. Następny
   gdy pętla widzi ten sam element, jego klucz istnieje w skrócie i
   wartość tego klucza to prawda (ponieważ nie jest to 0 ani „undef”), więc
   next pomija tę iterację i pętla przechodzi do następnego elementu.

       my @unique = ();
       mój% widziany = ();

       foreach my $ elem (@array)
       {
         następny jeśli $ widziany {$ elem} ++;
         push @unique, $ elem;
       }

   Możesz napisać to krócej, używając polecenia grep, co robi to samo
   rzecz.

       mój% widziany = ();
       my @unique = grep {! $ widziano {$ _} ++} @array;
John Siracusa
źródło
17
John iz in mah anzers stealing mah rep!
brian d foy
5
Myślę, że powinieneś otrzymać dodatkowe punkty za faktyczne sprawdzenie pytania.
Brad Gilbert,
2
Podoba mi się, że najlepsza odpowiedź to 95% kopiuj-wklej i 3 zdania OC. Aby być całkowicie jasne, to jest najlepsza odpowiedź; Po prostu uważam ten fakt za zabawny.
Parthian Shot
70

Lista instalacji :: MoreUtils z CPAN

Następnie w swoim kodzie:

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);
Ranguard
źródło
4
Fakt, że List :: MoreUtils nie jest spakowany w / perl trochę szkodzi przenośności projektów, które go używają :( (nie zrobię tego)
yPhil
3
@Ranguard: @dup_listpowinno być wewnątrz uniqrozmowy, a nie@dups
incutonez
@yassinphilip CPAN jest jedną z rzeczy, które sprawiają, że Perl jest tak potężny i wspaniały, jak tylko może być. Jeśli piszesz swoje projekty wyłącznie w oparciu o podstawowe moduły, nakładasz ogromne ograniczenia na swój kod, wraz z prawdopodobnie napisanym kodem, który próbuje zrobić to, co niektóre moduły robią znacznie lepiej, tylko po to, aby ich nie używać. Ponadto używanie modułów podstawowych niczego nie gwarantuje, ponieważ różne wersje Perla mogą dodawać lub usuwać moduły podstawowe z dystrybucji, więc przenośność nadal zależy od tego.
Francisco Zarabozo
24

Mój zwykły sposób to:

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

Jeśli używasz hasha i dodajesz elementy do hasha. Masz również bonus, wiedząc, ile razy każdy element pojawia się na liście.

Xetius
źródło
2
Ma to tę wadę, że nie zachowuje oryginalnej kolejności, jeśli jest to potrzebne.
Nathan Fellman
Lepiej jest używać plasterków zamiast foreachpętli:@unique{@myarray}=()
Onlyjob
8

Zmienna @array to lista ze zduplikowanymi elementami

%seen=();
@unique = grep { ! $seen{$_} ++ } @array;
Sreedhar
źródło
7

Można to zrobić za pomocą prostego jednego wkładu 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.

Blok PFM robi to:

Dane w @in są wprowadzane do MAP. MAP buduje anonimowy hash. Klucze są wyodrębniane z skrótu i ​​przesyłane do @out

Jastrząb
źródło
4

Ten ostatni był całkiem niezły. Po prostu trochę go poprawię:

my @arr;
my @uniqarr;

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

Myślę, że jest to prawdopodobnie najbardziej czytelny sposób, aby to zrobić.

jh314
źródło
4

Metoda 1: Użyj skrótu

Logika: Hash może mieć tylko unikalne klucze, więc iteruj po tablicy, przypisz dowolną wartość do każdego elementu tablicy, zachowując element jako klucz tego skrótu. Klucze zwrotne skrótu, to twoja unikalna tablica.

my @unique = keys {map {$_ => 1} @array};

Metoda 2: Rozszerzenie metody 1 o możliwość ponownego wykorzystania

Lepiej zrobić podprogram, jeśli mamy używać tej funkcji wiele razy w naszym kodzie.

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

Metoda 3: Użyj modułu List::MoreUtils

use List::MoreUtils qw(uniq);
my @unique = uniq(@array);
Kamal Nayan
źródło
1

Poprzednie odpowiedzi w dużym stopniu podsumowują możliwe sposoby wykonania tego zadania.

Jednak proponuję modyfikację dla tych, którzy nie dbają o liczenie duplikaty, ale zrobić dbają o zamówieniu.

my @record = qw( yeah I mean uh right right uh yeah so well right I maybe );
my %record;
print grep !$record{$_} && ++$record{$_}, @record;

Zwróć uwagę, że poprzednio sugerowane grep !$seen{$_}++ ...przyrosty $seen{$_}przed zaprzeczeniem, więc przyrost zachodzi niezależnie od tego, czy już był, %seenczy nie. Powyższe jednak $record{$_}powoduje zwarcia, gdy jest prawdą, pozostawiając to, co kiedyś usłyszano %record.

Możesz też pójść na tę śmieszność, która wykorzystuje autowifikację i istnienie kluczy mieszających:

...
grep !(exists $record{$_} || undef $record{$_}), @record;

Może to jednak prowadzić do pewnego zamieszania.

A jeśli nie obchodzi Cię ani kolejność, ani liczba duplikatów, możesz zrobić kolejny hack za pomocą plastrów z krzyżykiem i sztuczki, o której wspomniałem:

...
undef @record{@record};
keys %record; # your record, now probably scrambled but at least deduped
YenForYang
źródło
Dla porównujących: sub uniq{ my %seen; undef @seen{@_}; keys %seen; } schludnie.
stevesliva
0

Spróbuj tego, wygląda na to, że funkcja uniq wymaga posortowanej listy, aby działać poprawnie.

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";
saschabeaumont
źródło
0

Korzystanie z koncepcji unikalnych kluczy skrótu:

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

Wyjście: acbd

Sandeep_black
źródło