La matriz tiene muchos datos y necesito eliminar dos elementos.
A continuación se muestra el fragmento de código que estoy usando,
my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
La matriz tiene muchos datos y necesito eliminar dos elementos.
A continuación se muestra el fragmento de código que estoy usando,
my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
Respuestas:
Utilice empalme si ya conoce el índice del elemento que desea eliminar.
Grep funciona si estás buscando.
Si necesita hacer muchos de estos, obtendrá un rendimiento mucho mejor si mantiene su matriz en orden ordenado, ya que luego puede hacer una búsqueda binaria para encontrar el índice necesario.
Si tiene sentido en su contexto, es posible que desee considerar el uso de un "valor mágico" para los registros eliminados, en lugar de eliminarlos, para ahorrar en el movimiento de datos; por ejemplo, establezca los elementos eliminados en undef. Naturalmente, esto tiene sus propios problemas (si necesita saber el número de elementos "en vivo", debe realizar un seguimiento de ellos por separado, etc.), pero puede valer la pena según su aplicación.
Editar En realidad, ahora que echo un segundo vistazo, no use el código grep anterior. Sería más eficiente encontrar el índice del elemento que desea eliminar, luego use splice para eliminarlo (el código que tiene acumula todos los resultados que no coinciden ..)
my $index = 0;
$index++ until $arr[$index] eq 'foo';
splice(@arr, $index, 1);
Eso eliminará la primera aparición. Eliminar todas las apariciones es muy similar, excepto que querrá obtener todos los índices en una sola pasada:
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
El resto se deja como ejercicio para el lector; recuerde que la matriz cambia a medida que la empalma.
Edit2 John Siracusa señaló correctamente que tenía un error en mi ejemplo ... arreglado, lo siento.
my ($index) = grep { $arr[$_] eq 'foo' } 0..$#arr; if (defined $index) {splice(@arr, $index, 1); }
- para el primer partido
¿Es esto algo que vas a hacer mucho? Si es así, es posible que desee considerar una estructura de datos diferente. Grep buscará en toda la matriz cada vez y una matriz grande podría ser bastante costosa. Si la velocidad es un problema, es posible que desee considerar usar un Hash en su lugar.
En su ejemplo, la clave sería el número y el valor sería el recuento de elementos de ese número.
si cambias
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
a
my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);
Esto evita el problema de la renumeración de la matriz eliminando primero los elementos de la parte posterior de la matriz. Poner un empalme () en un bucle foreach limpia @arr. Relativamente simple y legible ...
foreach $item (@del_indexes) {
splice (@arr,$item,1);
}
Puede usar el corte de matriz en lugar de empalmar. Grep para devolver los índices que desea conservar y usar el corte:
my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];
@arr = @arr[grep ...]
que me gusta particularmente. No estoy seguro de cuán eficiente es, pero voy a empezar a usarlo porque no puede ser peor que las otras soluciones.
El resto de la publicación documenta la dificultad de convertir las pruebas de elementos en splice
compensaciones. Por lo tanto, haciéndolo más completo respuesta .
Mire los giros por los que tiene que pasar para tener un algoritmo eficiente (es decir, de una pasada) para convertir las pruebas en los elementos de la lista en índices. Y no es tan intuitivo en absoluto.
sub array_remove ( \@& ) {
my ( $arr_ref, $test_block ) = @_;
my $sp_start = 0;
my $sp_len = 0;
for ( my $inx = 0; $inx <= $#$arr_ref; $inx++ ) {
local $_ = $arr_ref->[$inx];
next unless $test_block->( $_ );
if ( $sp_len > 0 && $inx > $sp_start + $sp_len ) {
splice( @$arr_ref, $sp_start, $sp_len );
$inx = $inx - $sp_len;
$sp_len = 0;
}
$sp_start = $inx if ++$sp_len == 1;
}
splice( @$arr_ref, $sp_start, $sp_len ) if $sp_len > 0;
return;
}
undef
. Además, del documento vinculado por ringø: "ADVERTENCIA: Se desaconseja encarecidamente llamar a delete en valores de matriz. La noción de eliminar o verificar la existencia de elementos de matriz de Perl no es conceptualmente coherente y puede conducir a un comportamiento sorprendente". (el párrafo anterior en el documento tiene todos los detalles sangrientos).
Elimina todas las apariciones de 'algo' si matriz.
Basado en las respuestas de SquareCog:
my @arr = ('1','2','3','4','3','2', '3','4','3');
my @dix = grep { $arr[$_] eq '4' } 0..$#arr;
my $o = 0;
for (@dix) {
splice(@arr, $_-$o, 1);
$o++;
}
print join("\n", @arr);
Cada vez que eliminemos un índice de @arr
, el siguiente índice correcto para eliminar será $_-current_loop_step
.
Lo mejor que encontré fue una combinación de "undef" y "grep":
foreach $index ( @list_of_indexes_to_be_skiped ) {
undef($array[$index]);
}
@array = grep { defined($_) } @array;
¡Eso hace el truco! Federico
grep
al final, los elimina.
Solo para estar seguro de haber comparado las soluciones grep y map, primero buscando índices de elementos coincidentes (los que se deben eliminar) y luego eliminando directamente los elementos mediante grep sin buscar los índices. Parece que la primera solución propuesta por Sam al hacer su pregunta ya era la más rápida.
use Benchmark;
my @A=qw(A B C A D E A F G H A I J K L A M N);
my @M1; my @G; my @M2;
my @Ashrunk;
timethese( 1000000, {
'map1' => sub {
my $i=0;
@M1 = map { $i++; $_ eq 'A' ? $i-1 : ();} @A;
},
'map2' => sub {
my $i=0;
@M2 = map { $A[$_] eq 'A' ? $_ : () ;} 0..$#A;
},
'grep' => sub {
@G = grep { $A[$_] eq 'A' } 0..$#A;
},
'grem' => sub {
@Ashrunk = grep { $_ ne 'A' } @A;
},
});
El resultado es:
Benchmark: timing 1000000 iterations of grem, grep, map1, map2...
grem: 4 wallclock secs ( 3.37 usr + 0.00 sys = 3.37 CPU) @ 296823.98/s (n=1000000)
grep: 3 wallclock secs ( 2.95 usr + 0.00 sys = 2.95 CPU) @ 339213.03/s (n=1000000)
map1: 4 wallclock secs ( 4.01 usr + 0.00 sys = 4.01 CPU) @ 249438.76/s (n=1000000)
map2: 2 wallclock secs ( 3.67 usr + 0.00 sys = 3.67 CPU) @ 272702.48/s (n=1000000)
M1 = 0 3 6 10 15
M2 = 0 3 6 10 15
G = 0 3 6 10 15
Ashrunk = B C D E F G H I J K L M N
Como lo muestran los tiempos transcurridos, es inútil intentar implementar una función de eliminación utilizando índices definidos por grep o por mapas. Simplemente grep-remove directamente.
Antes de probar, pensaba que "map1" sería el más eficiente ... Supongo que debería confiar más en Benchmark. ;-)
Si conoce el índice de la matriz, puede eliminarlo () . La diferencia entre splice () y delete () es que delete () no vuelve a numerar los elementos restantes de la matriz.
Un código similar que escribí una vez para eliminar cadenas que no comienzan con SB.1 de una matriz de cadenas
my @adoSymbols=('SB.1000','RT.10000','PC.10000');
##Remove items from an array from backward
for(my $i=$#adoSymbols;$i>=0;$i--) {
unless ($adoSymbols[$i] =~ m/^SB\.1/) {splice(@adoSymbols,$i,1);}
}