• Publicidad

Eliminar repetidos

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Eliminar repetidos

Notapor Maku79 » 2018-03-28 10:24 @475

¡¡Hola de nuevo!!

Soy mega nueva en este lenguaje. Hasta hace nada iba con los arrays y ahora ya me estoy lanzando con el "hash". :roll:

Sigo jugando y me invento cosas a ver si puedo conseguirlas pues creo que con este lenguaje se puede hacer de todo, jejeje. :D Pero voy poco a poco.

A ver si explico bien lo que pretendo.

Antes de nada comentar que he visto los manuales que me indicó explorer del manual en inglés y en español (http://perldoc.perl.org/perlfaq4.html#H ... r-array%3f) y a pesar de seguirlos, no consigo eliminar los duplicados. Me da que el problema lo tengo en la forma de hacer el bucle, pero tras probar con varios "print" de variables, sigo sin ver dónde está el problema.

Tengo un fichero por bloques:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
11113333444455552222,asdjjdssldl sldksld sldksl
444455556666677778, dfrtrsas lllabbla jjjewe
1234,dgstrs jj.op
4321,uytfg/a

012345678911223344556,fdflstls kktllb qqqqqq dddrrete
98765432199887766554,ñlkjkht sttrvmns c.f.
5643,lloopp ddddddttttt
9087,noseque nosecuantos
 
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

y así sucesivamente.

Dentro de este fichero inicial ya consigo sacar en otro fichero las primeras líneas de cada bloque, es decir:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
11113333444455552222,asdjjdssldl sldksld sldksl
012345678911223344556,fdflstls kktllb qqqqqq dddrrete
 
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Bien, pero es que en el fichero inicial, esta primera línea se repite, y quiero que solo la copie en el fichero destino una vez.

Copio el código que he usado (en esta versión lo imprime por pantalla porque me es más sencillo comprobar el resultado).
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/local/bin/perl
  2. use warnings;
  3. use strict;
  4. use autodie;
  5. #use List::MoreUtils qw(uniq);
  6.  
  7. my $end = "End of File!\n\n";
  8. my $filename = 'fichero.txt';
  9. open my $filehandle, '<', $filename or die $!;
  10. open my $filehandle2, '<', $filename or die $!;
  11.  
  12. my @array = <$filehandle>;
  13. my @array2 = <$filehandle2>;
  14. my $i=0;
  15. $array[$i] = $.;
  16. print "\n Número de líneas del fichero $filename: $array[$i]\t";
  17. print $end;
  18.  
  19. for (my $j = 0; $j <= $array[$i]; $j+=5) {
  20.     my @unique = ();
  21.     my %seen;
  22.     foreach my $elem (@array2[$j]) {
  23.         #print $array2[$j];
  24.  
  25.         push @unique, $elem if not $seen{$elem}++;
  26.                 foreach (@unique){
  27.                         print;
  28.                 }
  29.     }
  30. }
  31.  
  32. close $filehandle;
  33. close $filehandle2;
  34.  
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4

El resultado que obtengo es: la primera línea de cada bloque pero me salen los repetidos también.

He probado varias opciones del manual del foro y, o pierdo valores o siguen saliendo los repetidos.

¿Alguna sugerencia? ¡Se agradece cualquier ayuda!

¡Muchas gracias!
Maku79
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2018-03-22 06:44 @322

Publicidad

Re: Eliminar repetidos

Notapor explorer » 2018-03-28 11:50 @534

Generalmente, cuando usas hashes, te ahorras algunos bucles. Y tu estás usando demasiados :wink:

Esta es la forma canónica de resolver el problema:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/local/bin/perl
  2. use v5.10.1;
  3. use strict;
  4. use warnings;
  5. use autodie;
  6. #use List::MoreUtils qw(uniq);
  7.  
  8. my $filename = 'code_40683.txt';
  9. my $end      = "End of File!\n\n";
  10.  
  11. open my $FH, '<', $filename;                    # con autodie no es necesario poner die()
  12. my @lineas  = <$FH>;                            # leemos todo el archivo
  13. chomp @lineas;                                  # quitar retornos de carro
  14. close $FH;                                      # cerrar archivo
  15.  
  16. # para todas las líneas, saltando de 5 en 5
  17. my %vista;                              # recuerdo de las líneas vistas
  18. for (my $i = 0; $i < @lineas; $i += 5) {        # suponemos que todos los bloques tienen 5 líneas
  19.  
  20.     my $linea = $lineas[$i];
  21.  
  22.     if (not $vista{ $linea }) {                 # si es la primera vez que la vemos
  23.         say $linea;                             # la sacamos
  24.     }
  25.    
  26.     $vista{ $linea }++;                         # sumamos una aparición más
  27. }
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

La técnica consiste en meter la línea dentro del hash, como clave. Y con un valor cualquiera (en este caso, vamos sumando 1 cada vez que se ve la misma línea, pero podría ser un valor constante cualquiera, excepto que se evalúe a falso o nulo).

Las líneas 22 a 26 se pueden reducir a
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.     # sacamos la línea si no  la hemos visto antes
  2.     say $linea         if not $vista{ $linea }++;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

que es lo mismo, pero se lee de otra manera.

El problema que hay en tu código es que en la
  • línea 12: no necesitas leer dos veces el mismo archivo. Una vez almacenado en @array, ya lo tienes en memoria. Y como no lo vas a modificar, puedes acceder a las líneas las veces que quieras
  • línea 20: estás declarando la variable @unique dentro del bucle principal, por lo que se pone a "0" por cada línea.
  • línea 21: lo mismo para $seen. De hecho, sobra una de estas variables (si una línea ya está vista, es que es única).
  • línea 22: el bucle en realidad solo recorre... un único valor: @array2[$j]. Además, está mal escrito. Debería ser $array2[$j]
  • En la línea 25 almacenas la línea si no la has visto antes. Esto es correcto, pero solo necesitas esta combinación de hash y array si quieres almacenar las líneas únicas antes de sacarlas en pantalla. Si te vale con sacarlas a medida de que te las encuentras, entonces no lo necesitas
  • líneas 26 a 28: deberían estar al final del programa, cuando ya tengamos toda la lista de líneas únicas.
Quedaría así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/local/bin/perl
  2. use warnings;
  3. use strict;
  4. use autodie;
  5.  
  6. my $filename = 'code_40683.txt';
  7.  
  8. open my $FH, '<', $filename;
  9. my @array = <$FH>;
  10. close $FH;
  11.  
  12. my @unique;
  13. my %seen;
  14.  
  15. for (my $j = 0; $j <= @array; $j += 5) {
  16.     my $elem = $array[$j];
  17.  
  18.     push @unique, $elem if not $seen{$elem}++;
  19. }
  20.  
  21. print "Número de líneas del archivo $filename: ", scalar(@array), "\n";
  22.  
  23. for (@unique) {
  24.         print;
  25. }
  26.  
  27. print "End of File!\n\n";
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Si no vamos a hacer nada más con las líneas, y solo nos interesa sacarlas por pantalla, nos vale con hacerlo todo a la vez. Quitamos toda referencia a @unique... y nos sale la primera solución.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Eliminar repetidos

Notapor Maku79 » 2018-03-29 03:22 @182

¡Muchas gracias por tus explicaciones, explorer! Igual hago fallos muy de principiante pero voy aprendiendo gracias al prueba y error, el foro y tus explicaciones. :D De nuevo, ¡muchas gracias!

Cuando puse los dos arrays (@array y @array2) lo hice porque no tenía manera de que me sacará el primer elemento en la primera iteración, de ahí que me "inventara" hacerlo en dos bucles, abriendo dos veces el mismo fichero y era tan fácil como recorrer correctamente la variable en el bucle... :roll: Además del "scalar(@array)", me inventé de todo para sacar el número de lineas del fichero :lol:

Lo de poner @unique y %seen dentro del bucle ya fue una prueba (entiendo que sin sentido) pero es que hice tantas pruebas que al final copié aquí el código incorrecto.

Ahora con las modificaciones ya no me salen los elementos duplicados :D Aquí mi código:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/local/bin/perl
  2. use v5.14;
  3. use warnings;
  4. use strict;
  5. use autodie;
  6. #use Data::Dumper;
  7.  
  8. my $end = "End of File!\n\n";
  9. my $filename = 'prueba2.txt';
  10. open my $filehandle, '<', $filename;
  11.  
  12. my @array = <$filehandle>;
  13. chomp @array;
  14. close $filehandle;
  15.  
  16. my @unique;
  17. my %seen;                                               #Para recordar las lineas vistas
  18. for (my $j = 0; $j < @array; $j+=5) {                   #Sacamos las lineas de 5 en 5
  19.         my $elem = $array[$j];
  20.         push @unique, $elem if not $seen{$elem}++;      #Guardamos en hash sin repeticiones
  21. }
  22.  
  23. print "\n Numero de lineas del fichero $filename: ", scalar(@array), "\n";
  24.  
  25. for (@unique){
  26.         print;                                          #Mostramos por pantalla el listado
  27. }
  28.  
  29. print $end;
  30.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Ahora viene el probar a volcar el hash a un fichero sin que me saque los pares de valores :roll:
¡Iré probando a ver si lo consigo! ¡A tope!

¡¡Muchas gracias de nuevo, explorer!!
Maku79
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2018-03-22 06:44 @322

Re: Eliminar repetidos

Notapor explorer » 2018-03-29 10:13 @467

Un problema que te vas a encontrar con los hashes, es que el almacenamiento de los pares clave/valor se hace de forma desordenada.

Mejor dicho: no es desordenada, sí que sigue un orden preciso, pero no debe ser conocido por el programador ni por nadie más (algoritmos de Hashing, claro).

A nivel práctico: obtendrás el listado de líneas únicas, pero... en un orden cuasi-aleatorio.

Si quieres mantener el orden en que aparecen en el archivo original, entonces sí que necesitas seguir usando un array.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Eliminar repetidos

Notapor Maku79 » 2018-04-03 02:39 @152

¡Hola, explorer!

El orden en que los vuelque en el fichero de salida no es importante, lo importante es que no salgan repetidos y entiendo que si quisiera algún orden se podría ordenar con un sort.

No obstante, no lo consigo, siempre salen o el valor completo de $VAR1 o el valor y en la siguiente línea el número de repeticiones.
He probado a recorrer los elementos con keys y con arrays, no sé qué hago mal :(

Este es el código que me muestra el valor y el número de repeticiones sin el "$VAR1" ni "=>":

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/local/bin/perl
  2. use v5.14;
  3. use warnings;
  4. use strict;
  5. use autodie;
  6.  
  7. my $end = "End of File!\n\n";
  8. my $filename = 'prueba2.txt';
  9. open my $filehandle, '<', $filename;
  10. open my $SALIDA, '>', 'salida.txt';
  11. my $elem;
  12.  
  13. my @array = <$filehandle>;
  14. chomp @array;
  15. close $filehandle;
  16.  
  17. our @unique;
  18. my %seen;                                               #Para recordar las lineas vistas
  19. for (my $j = 0; $j < @array; $j+=5) {                   #Sacamos las lineas de 5 en 5
  20.         $elem = $array[$j];
  21.         push @unique, $elem if not $seen{$elem}++;      #Guardamos en hash sin repeticiones
  22. }
  23.  
  24. print "\n Numero de lineas del fichero $filename: ", scalar(@array), "\n";
  25.  
  26. print {$SALIDA} join "\n", %seen;                       #Volcamos a fichero los valores no repetidos
  27. close $SALIDA;
  28. print $end;
  29.  
Coloreado en 0.013 segundos, usando GeSHi 1.0.8.4

Este es el código que me vuelca el contenido completo del hash ($VAR1):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/local/bin/perl
  2. use v5.14;
  3. use warnings;
  4. use strict;
  5. use autodie;
  6. use Data::Dumper;
  7.  
  8. my $end = "End of File!\n\n";
  9. my $filename = 'prueba2.txt';
  10. open my $filehandle, '<', $filename;
  11. my $elem;
  12.  
  13. my @array = <$filehandle>;
  14. chomp @array;
  15. close $filehandle;
  16.  
  17. my @unique;
  18. our %seen;                                              #Para recordar las lineas vistas
  19. for (my $j = 0; $j < @array; $j+=5) {                   #Sacamos las lineas de 5 en 5
  20.         $elem = $array[$j];
  21.         push @unique, $elem if not $seen{$elem}++;      #Guardamos en hash sin repeticiones
  22. }
  23.  
  24. print "\n Numero de lineas del fichero $filename: ", scalar(@array), "\n";
  25.  
  26.  
  27. open my $ofh, '>', "tmp.txt";
  28. print $ofh Data::Dumper->Dump([ \%seen ]);
  29. close $ofh;
  30.  
  31. undef %seen;
  32. do "./tmp.txt";
  33.  
  34. #print Data::Dumper->Dump([ \%seen ],['*seen']);  #Lo muestra por pantalla
  35. print $end;
  36.  
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4

Y este último me vuelca el primer valor por pantalla (es que lo pongo así porque me es más sencillo verlo que ir abriendo y cerrando el fichero de salida cada vez) y luego da error:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/local/bin/perl
  2. use v5.14;
  3. use warnings;
  4. use strict;
  5. use autodie;
  6.  
  7. my $end = "End of File!\n\n";
  8. my $filename = 'prueba2.txt';
  9. open my $filehandle, '<', $filename;
  10. open my $SALIDA, '>', 'salida.txt';
  11. my $elem;
  12.  
  13. my @array = <$filehandle>;
  14. chomp @array;
  15. close $filehandle;
  16.  
  17. my @unique;
  18. our %seen;                                              #Para recordar las lineas vistas
  19. for (my $j = 0; $j < @array; $j+=5) {                   #Sacamos las lineas de 5 en 5
  20.         $elem = $array[$j];
  21.         push @unique, $elem if not $seen{$elem}++;      #Guardamos en hash sin repeticiones
  22. }
  23.  
  24. print "\n Numero de lineas del fichero $filename: ", scalar(@array), "\n";
  25.  
  26. for my $data (keys %seen){
  27.         print $data, "\n";
  28.         for my $valor ( @{ $seen{$data} } ) {
  29.                 print "\t$valor\n\n";
  30.         }
  31. }
  32.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Está claro que no muestro correctamente el hash, o hay algo que se me escapa...

He estado mirando varios enlaces como por ejemplo https://perldoc.perl.org/perldsc.html#D ... -OF-ARRAYS pero no consigo lo que quiero ¿Alguna sugerencia?

¡Millones de gracias! Siento tanta pregunta pero es que no sé qué hago mal...
Maku79
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2018-03-22 06:44 @322

Re: Eliminar repetidos

Notapor explorer » 2018-04-03 06:59 @333

En mi primer ejemplo sí que salen los valores únicos, además de quedar almacenado en el hash.

Si solo quieres sacar los valores únicos almacenados en el hash %seen, entonces te vale con recorrer las claves, que es donde se han guardado los valores únicos:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. for my $clave (keys %seen) {
  2.     say $SALIDA $clave;
  3. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

No sé si era esto lo que preguntabas...
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Eliminar repetidos

Notapor Maku79 » 2018-04-04 02:06 @129

Sí, sí, si lo de los valores únicos sí que me sale desde que me lo has explicado, lo que no consigo es volcar el valor del hash en un fichero.

Cuando me refiero a los elementos repetidos es al número que sale:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
$VAR1 = (
          '11113333444455552222,asdjjdssldl sldksld sldksl' => 2,            ### Me dice que esa línea aparece 2 veces pero solo la muestra 1
          '012345678911223344556,fdflstls kktllb qqqqqq dddrrete' => 1,      ### Me dice que esa linea aparece una vez
        );
 
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

En mi último ejemplo también uso el for que comentas pero por lo visto me he complicado la vida... ¡no sé cómo lo hago! :roll:

Finalmente ya consigo lo que necesitaba :D

¡Muchas gracias, explorer! ¡y gracias por tu paciencia!
Maku79
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2018-03-22 06:44 @322

Re: Eliminar repetidos

Notapor Maku79 » 2018-11-30 06:16 @302

¡¡Hola de nuevo!!

Hace tiempo que no vengo por estos lares pero de momento tenía resueltas casi todas mis dudas con las cosillas que necesito de Perl hasta hoy.

Este mismo código que pongo más arriba que me ha estado funcionando a la perfección, he cambiado el fichero fuente por uno muuuuucho más grande y cuál ha sido mi sorpresa al ver que en el resultado me sacaba valores de cualquier línea, no solo de la línea 2 que es la que me interesa, tal como está en el bucle for:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. for (my $j = 1; $j < @array; $j+=5) {                   # Sacamos las líneas de 5 en 5
  2.         $elem = $array[$j];
  3.         #print $array[$j],"\n";
  4.         push @unique, $elem if not $seen{$elem}++;      # Guardamos en hash sin repeticiones
  5. }
  6. my $contador = 0;
  7. for my $clave (keys %seen) {
  8.     say $SALIDA $clave;
  9.     $contador++;
  10. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

No entiendo el porqué. El fichero origen tiene la misma estructura, bloques de 5 líneas.

He creado un nuevo fichero copiando secciones de forma aleatoria (el fichero entonces queda más pequeño) y sí funciona :?: :?:

¿Existe alguna limitación? ¿Por qué cuando el fichero es más pequeño sí funciona pero es grande, no?

¿Hay algo mal en el código?

¡¡Gracias por adelantado!!
Maku79
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2018-03-22 06:44 @322

Re: Eliminar repetidos

Notapor explorer » 2018-12-01 14:19 @638

Si cambia el comportamiento al cambiar de archivo, es posible que lo que esté mal sea nuestra interpretación de que el segundo archivo esté como esperamos que esté, al igual que el primer archivo. O sea, que nuestro programa esté bien, pero no la entrada al programa.

Si hay un fallo en la lectura de líneas desde el segundo archivo, ¿seguro que el formato es correcto? Me refiero a si los caracteres invisibles del final de línea son coherentes con tu sistema operativo.

Analiza el formato del archivo, sobre todo en los alrededores de la línea donde empieza a fallar. Mira con un buen editor de textos o con un editor hexadecimal cuáles son los caracteres que están al final de las líneas.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Eliminar repetidos

Notapor Maku79 » 2018-12-03 08:17 @387

¡Gracias, explorer!

Miraré a ver pero el entorno de trabajo es el mismo, no ha cambiado nada, solo el fichero fuente.

Es un txt y lo edito con el Notepad++.

Voy a intentar cambiar la codificación del fichero a ver si consigo algo.

¡Muchas gracias, no obstante!
Maku79
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2018-03-22 06:44 @322

Siguiente

Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: Bing [Bot] y 1 invitado

cron