¿Cómo elimino elementos duplicados de una matriz en Perl?

156

Tengo una matriz en Perl:

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

¿Cómo elimino los duplicados de la matriz?

David
fuente

Respuestas:

168

Puede hacer algo como esto como se demuestra en perlfaq4 :

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

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

print "@filtered\n";

Salidas:

one two three

Si desea usar un módulo, pruebe la uniqfunción deList::MoreUtils

Greg Hewgill
fuente
28
por favor no use $
ao
2
Es un myléxico en este ámbito, por lo que está bien. Dicho esto, posiblemente podría elegirse un nombre de variable más descriptivo.
Ephemient
2
@ephemient sí, pero si tuviera que agregar la clasificación en esta función, entonces triunfaría $::ay $::b, ¿no?
vol7ron
55
@BrianVandenberg Bienvenido al mundo de 1987, cuando esto se creó, y casi el 100% de compatibilidad con perl para Perl, por lo que no se puede eliminar.
szabgab
18
sub uniq { my %seen; grep !$seen{$_}++, @_ }es una mejor implementación ya que conserva el orden sin costo. O incluso mejor, use el de List :: MoreUtils.
ikegami el
120

La documentación de Perl viene con una buena colección de preguntas frecuentes. Su pregunta se hace con frecuencia:

% perldoc -q duplicate

La respuesta, copiar y pegar de la salida del comando anterior, aparece a continuación:

Encontrado en /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
 ¿Cómo puedo eliminar elementos duplicados de una lista o matriz?
   (contribuido por brian d foy)

   Usa un hash. Cuando piense que las palabras son "únicas" o "duplicadas", piense
   "claves hash".

   Si no te importa el orden de los elementos, podrías simplemente
   crea el hash y luego extrae las claves. No importa cómo
   crea ese hash: solo que usas "claves" para obtener los elementos únicos.

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

       my @unique = keys% hash;

   Si desea utilizar un módulo, pruebe la función "uniq" de
   "Lista :: MoreUtils". En el contexto de la lista, devuelve los elementos únicos,
   preservando su orden en la lista. En contexto escalar, devuelve el
   Número de elementos únicos.

       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

   También puede revisar cada elemento y omitir los que ha visto.
   antes de. Use un hash para realizar un seguimiento. La primera vez que el bucle ve un
   elemento, ese elemento no tiene clave en% Visto. La declaración "siguiente" crea
   la clave e inmediatamente usa su valor, que es "undef", por lo que el bucle
   continúa al "empuje" e incrementa el valor de esa clave. El siguiente
   cada vez que el bucle ve ese mismo elemento, su clave existe en el hash y
   el valor para esa clave es verdadero (ya que no es 0 o "undef"), entonces el
   siguiente omite esa iteración y el bucle va al siguiente elemento.

       my @unique = ();
       mi% visto = ();

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

   Puedes escribir esto más brevemente usando un grep, que hace lo mismo
   cosa.

       mi% visto = ();
       my @unique = grep {! $ visto {$ _} ++} @array;
John Siracusa
fuente
17
John iz en mah anzers robando mah rep!
brian d foy
55
Creo que deberías obtener puntos de bonificación por buscar realmente la pregunta.
Brad Gilbert
2
Me gusta que la mejor respuesta es 95% copiar y pegar y 3 oraciones de OC. Para ser perfectamente claro, esta es la mejor respuesta; Simplemente encuentro ese hecho divertido.
Parthian Shot
70

Instalar :: listado de MoreUtils desde CPAN

Luego en tu código:

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
fuente
44
El hecho de que List :: MoreUtils no está incluido con perl daña un poco la portabilidad de los proyectos que lo usan :( (por mi parte, no)
yPhil
3
@Ranguard: @dup_listdebería estar dentro de la uniqllamada, no@dups
incutonez
@yassinphilip CPAN es una de las cosas que hacen que Perl sea lo más poderoso y excelente posible. Si está escribiendo sus proyectos basados ​​solo en módulos centrales, está poniendo un límite enorme en su código, junto con un código posiblemente vertiginoso que intenta hacer lo que algunos módulos hacen mucho mejor solo para evitar usarlos. Además, el uso de módulos principales no garantiza nada, ya que las diferentes versiones de Perl pueden agregar o eliminar módulos principales de la distribución, por lo que la portabilidad aún depende de eso.
Francisco Zarabozo
24

Mi forma habitual de hacer esto es:

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

Si usa un hash y agrega los elementos al hash. También tiene la ventaja de saber cuántas veces aparece cada elemento en la lista.

Xetius
fuente
2
Esto tiene el inconveniente de no preservar el pedido original, si lo necesita.
Nathan Fellman
Es mejor usar rebanadas en lugar de foreachbucle:@unique{@myarray}=()
Onlyjob
8

La variable @array es la lista con elementos duplicados.

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

Se puede hacer con un simple 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.

El bloque PFM hace esto:

Los datos en @in se introducen en MAP. MAP crea un hash anónimo. Las claves se extraen del hash y se introducen en @out

Halcón
fuente
4

Ese último fue bastante bueno. Solo lo modificaría un poco:

my @arr;
my @uniqarr;

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

Creo que esta es probablemente la forma más fácil de hacerlo.

jh314
fuente
4

Método 1: usar un hash

Lógica: un hash solo puede tener claves únicas, por lo que iterar sobre la matriz, asignar cualquier valor a cada elemento de la matriz, manteniendo el elemento como clave de ese hash. Teclas de retorno del hash, es su matriz única.

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

Método 2: extensión del método 1 para reutilización

Es mejor hacer una subrutina si se supone que debemos usar esta funcionalidad varias veces en nuestro código.

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

Método 3: usar el módulo List::MoreUtils

use List::MoreUtils qw(uniq);
my @unique = uniq(@array);
Kamal Nayan
fuente
1

Las respuestas anteriores resumen bastante bien las posibles formas de llevar a cabo esta tarea.

Sin embargo, sugiero una modificación para aquellos a quienes no les importa contar los duplicados, pero sí les importa el orden.

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

Tenga en cuenta que los grep !$seen{$_}++ ...incrementos sugeridos anteriormente $seen{$_}antes de negar, por lo que el incremento se produce independientemente de si ya ha sido %seeno no. Lo anterior, sin embargo, hace un cortocircuito cuando $record{$_}es cierto, dejando lo que se escuchó una vez "fuera de %record".

También podría optar por esta ridiculez, que aprovecha la autovivificación y la existencia de claves hash:

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

Eso, sin embargo, podría generar cierta confusión.

Y si no le importa ni el orden ni el recuento duplicado, podría usar otro truco utilizando trozos de hash y el truco que acabo de mencionar:

...
undef @record{@record};
keys %record; # your record, now probably scrambled but at least deduped
YenForYang
fuente
Para los que comparan: sub uniq{ my %seen; undef @seen{@_}; keys %seen; } aseado.
stevesliva
0

Pruebe esto, parece que la función uniq necesita una lista ordenada para funcionar correctamente.

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
fuente
0

Usando el concepto de claves hash únicas:

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

Salida: acbd

Sandeep_black
fuente