¿Cómo puedo mantener el orden de las claves que agrego a un hash Perl?
data-structures perl-data-structures (6)
¿Cómo puedo mantener el orden de la lista real después de contar su ocurrencia utilizando un hash en el siguiente programa? Por ejemplo, <DATA>
son
a
b
e
a
c
d
a
c
d
b
etc.
Usando hash, conté la ocurrencia de cada elemento.
y lo que quiero es:
a 3
b 2
e 1
c 2
d 2
pero el siguiente programa me muestra lo contrario.
my (%count, $line, @array_1, @array_2);
while ($line = <DATA>) {
$count{$line}++ if ( $line =~ //S/ );
}
@array_1 = keys(%count);
@array_2 = values(%count);
for(my $i=0; $i<$#array_1; $i++)
{
print "$array_1[$i]/t $array_2[$i]";
}
Desde la respuesta de perlfaq4 a "¿Cómo puedo hacer que mi hash recuerde el orden en el que puse elementos?"
¿Cómo puedo hacer que mi hash recuerde el orden en que puse elementos en él?
Use el Tie :: IxHash de CPAN.
use Tie::IxHash;
tie my %myhash, ''Tie::IxHash'';
for (my $i=0; $i<20; $i++) {
$myhash{$i} = 2*$i;
}
my @keys = keys %myhash;
# @keys = (0,1,2,3,...)
Los datos en una tabla hash se almacenan en orden del código hash de las teclas, que para la mayoría de los propósitos es como un orden aleatorio. También desea almacenar el orden de la primera aparición de cada tecla. Aquí hay una forma de abordar este problema:
my (%count, $line, @display_order);
while ($line = <DATA>) {
chomp $line; # strip the /n off the end of $line
if ($line =~ //S/) {
if ($count{$line}++ == 0) {
# this is the first time we have seen the key "$line"
push @display_order, $line;
}
}
}
# now @display_order holds the keys of %count, in the order of first appearance
foreach my $key (@display_order)
{
print "$key/t $count{$key}/n";
}
Los hashes no están ordenados, pero como de costumbre, CPAN ofrece una solución: Tie :: IxHash
use Tie::IxHash;
my %count;
tie %count, ''Tie::IxHash'';
while ($line = <DATA>) {
$count{$line}++ if ( $line =~ //S/ );
}
while( my( $key, $value)= each %count) {
print "$key/t $value";
}
Simplemente:
my (%count, @order);
while(<DATA>) {
chomp;
push @order, $_ unless $count{$_}++;
}
print "$_ $count{$_}/n" for @order;
__DATA__
a
b
e
a
c
d
a
c
d
b
No estoy convencido de que esta sea siempre una técnica mejor, pero a veces la he usado. En lugar de tener el tipo de hash "visto", puede almacenar tanto el recuento como el orden notado.
Básicamente, en lugar de $count{$line}
teniendo el número de veces visto, $count{$line}{count}
es el tiempo visto y $count{$line}{order}
es el orden en que se vio.
my %count;
while (my $line = <DATA>) {
chomp $line;
if ($line =~ //S/) {
$count{$line} ||= { order => scalar(keys(%count)) };
$count{$line}{count}++;
}
}
for my $line (sort { $count{$a}{order} <=> $count{$b}{order} } keys %count ) {
print "$line $count{$line}{count}/n";
}
Otra opción es el módulo puro perl Hash::Ordered
David Golden (@xdg). Obtienes orden pero es más lento ya que el hash se convierte en un objeto detrás de escena y usas métodos para acceder y modificar los elementos hash.
Probablemente haya puntos de referencia que puedan cuantificar cuánto más lento es el módulo que los hashes regulares, pero es una buena manera de trabajar con estructuras de datos clave / valor en pequeños guiones y lo suficientemente rápido para mí en ese tipo de aplicaciones. La documentación menciona varios otros enfoques para ordenar un hash también.