Come rimuovo gli elementi duplicati da un array in Perl?


156

Ho un array in Perl:

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

Come rimuovo i duplicati dall'array?

Risposte:


168

Puoi fare qualcosa del genere come dimostrato in perlfaq4 :

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

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

print "@filtered\n";

Uscite:

one two three

Se si desidera utilizzare un modulo, provare la uniqfunzione daList::MoreUtils


28
per favore non usare $ a o $ b negli esempi in quanto sono i
globi

2
È un mylessico in questo ambito, quindi va bene. Detto questo, potrebbe essere scelto un nome variabile più descrittivo.
effimero

2
@ephemient sì, ma se dovessi aggiungere l'ordinamento in questa funzione, avrebbe la briscola $::ae $::bno?
vol7ron,

5
@BrianVandenberg Benvenuti nel mondo del 1987 - quando è stato creato - e quasi il 100% di complicità di backword per il perl - quindi non può essere eliminato.
szabgab,

18
sub uniq { my %seen; grep !$seen{$_}++, @_ }è un'implementazione migliore poiché conserva l'ordine a costo zero. O ancora meglio, usa quello di List :: MoreUtils.
ikegami,

120

La documentazione Perl include una bella raccolta di domande frequenti. La tua domanda è frequente:

% perldoc -q duplicate

La risposta, copiata e incollata dall'output del comando sopra, appare sotto:

Trovato in /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
 Come posso rimuovere elementi duplicati da un elenco o da un array?
   (contributo di brian d foy)

   Usa un hash. Quando pensi alle parole "unico" o "duplicato", pensa
   "chiavi hash".

   Se non ti interessa l'ordine degli elementi, potresti semplicemente
   creare l'hash quindi estrarre le chiavi. Non è importante come te
   crea quell'hash: basta usare le "chiavi" per ottenere gli elementi univoci.

       my% hash = map {$ _, 1} @array;
       # o una sezione hash: @hash {@array} = ();
       # o un foreach: $ hash {$ _} = 1 foreach (@array);

       my @unique = keys% hash;

   Se si desidera utilizzare un modulo, provare la funzione "uniq" da
   "list :: moreutils". Nel contesto dell'elenco restituisce gli elementi univoci,
   preservando il loro ordine nell'elenco. Nel contesto scalare, restituisce il
   numero di elementi unici.

       usa 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

   Puoi anche passare attraverso ogni elemento e saltare quelli che hai visto
   prima. Usa un hash per tenere traccia. La prima volta che il loop vede un
   elemento, quell'elemento non ha chiave in% Visto. Viene creata l'istruzione "next"
   la chiave e usa immediatamente il suo valore, che è "undef", quindi il ciclo
   continua a "premere" e incrementa il valore per quel tasto. Il prossimo
   quando il loop vede lo stesso elemento, la sua chiave esiste nell'hash e
   il valore per quella chiave è vero (poiché non è 0 o "undef"), quindi il
   successivo salta quell'iterazione e il ciclo passa all'elemento successivo.

       my @unique = ();
       my% seen = ();

       foreach my $ elem (@array)
       {
         poi se $ visto {$ elem} ++;
         push @unique, $ elem;
       }

   Puoi scriverlo più brevemente usando un grep, che fa lo stesso
   cosa.

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


17
John iz in mah anzers ruba mah rep!
brian d foy,

5
Penso che dovresti ottenere punti bonus per aver effettivamente cercato la domanda.
Brad Gilbert,

2
Mi piace che la risposta migliore sia il 95% di copia-incolla e 3 frasi di OC. Per essere perfettamente chiari, questa è la risposta migliore; Trovo questo fatto divertente.
Parthian Shot

70

Elenco di installazione :: MoreUtils da CPAN

Quindi nel tuo codice:

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
Il fatto che List :: MoreUtils non sia in bundle w / perl kinda danneggia la portabilità dei progetti che lo utilizzano :( (I per uno non lo farà)
yPhil

3
@Ranguard: @dup_listdovrebbe essere all'interno della uniqchiamata, non@dups
incutonez

@yassinphilip CPAN è una delle cose che rendono Perl il più potente e grande possibile. Se stai scrivendo i tuoi progetti basandoti solo su moduli core, stai ponendo un enorme limite al tuo codice, insieme a un codice scritto possibilmente che tenta di fare ciò che alcuni moduli fanno molto meglio solo per evitare di usarli. Inoltre, l'utilizzo di moduli core non garantisce nulla, poiché diverse versioni di Perl possono aggiungere o rimuovere moduli core dalla distribuzione, quindi la portabilità dipende ancora da questo.
Francisco Zarabozo,

24

Il mio solito modo di farlo è:

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

Se usi un hash e aggiungi gli elementi all'hash. Hai anche il vantaggio di sapere quante volte ogni elemento appare nell'elenco.


2
Questo ha il rovescio della medaglia di non conservare l'ordine originale, se ne hai bisogno.
Nathan Fellman,

È meglio usare le fette invece del foreachciclo:@unique{@myarray}=()
Onlyjob

8

La variabile @array è l'elenco con elementi duplicati

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

7

Può essere fatto con un semplice liner 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.

Il blocco PFM fa questo:

I dati in @in vengono inseriti in MAP. MAP crea un hash anonimo. Le chiavi vengono estratte dall'hash e inserite in @out


4

Quest'ultimo è stato abbastanza buono. Vorrei solo modificarlo un po ':

my @arr;
my @uniqarr;

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

Penso che questo sia probabilmente il modo più leggibile per farlo.


4

Metodo 1: utilizzare un hash

Logica: un hash può avere solo chiavi univoche, quindi iterare sull'array, assegnare qualsiasi valore a ciascun elemento dell'array, mantenendo l'elemento come chiave di quell'hash. Restituisce le chiavi dell'hash, è il tuo array unico.

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

Metodo 2: estensione del metodo 1 per la riusabilità

Meglio creare una subroutine se dovremmo usare questa funzionalità più volte nel nostro codice.

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

Metodo 3: utilizzare il modulo List::MoreUtils

use List::MoreUtils qw(uniq);
my @unique = uniq(@array);

1

Le risposte precedenti riassumono praticamente i possibili modi per svolgere questo compito.

Tuttavia, suggerisco una modifica per coloro che non si preoccupano di contare i duplicati, ma si preoccupano dell'ordine.

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

Si noti che gli grep !$seen{$_}++ ...incrementi precedentemente suggeriti $seen{$_}prima di annullare, quindi l'incremento si verifica indipendentemente dal fatto che sia già stato %seeno meno. Quanto sopra, tuttavia, mette in corto circuito quando $record{$_}è vero, lasciando ciò che è stato ascoltato una volta "spento %record".

Potresti anche optare per questo ridicolo, che sfrutta l'autovivificazione e l'esistenza delle chiavi hash:

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

Ciò, tuttavia, potrebbe creare confusione.

E se non ti interessa né l'ordine né il conteggio dei duplicati, potresti fare un altro hack usando le porzioni di hash e il trucco che ho appena citato:

...
undef @record{@record};
keys %record; # your record, now probably scrambled but at least deduped

Per quelli a confronto: sub uniq{ my %seen; undef @seen{@_}; keys %seen; } pulito.
stevesliva,

0

Prova questo, sembra che la funzione uniq abbia bisogno di un elenco ordinato per funzionare correttamente.

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

Utilizzando il concetto di chiavi hash uniche:

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

Uscita: acbd

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.