• Publicidad

Ordenar hash compuesto

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

Ordenar hash compuesto

Notapor xagutxu_perez » 2008-06-13 06:57 @331

Hola a todos:

Me encuentro con un problema que no sé cómo resolver. He intentado hacer con bucles for, pero es muy lioso y creo que debe haber un modo mejor. Ojalá puedan ayudarme.

Tengo un hash formado por 3 array de 10 elementos cada uno:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
%hash = (scores => (1 3 1 2 3 3 1 5 2 3),
              grupos => (A G H V E T D A A H),
              marcas => (X X Y X Y Y Y Y X X));
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


Me gustaría poder ordenar los array del hash, fijándonos solo en los scores, de menor a mayor; con lo que quedaría:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
%hash = (scores => (1 1 1 2 2 3 3 3 3 5),
              grupos => (A H D V A G E T H A),
              marcas => (X Y Y X X X Y Y X Y));
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


¿Se os ocurre alguna forma eficiente de hacerlo?

Gracias, como siempre,

Xagutxu
xagutxu_perez
Perlero nuevo
Perlero nuevo
 
Mensajes: 43
Registrado: 2008-04-04 03:56 @206

Publicidad

Notapor monoswim » 2008-06-13 08:09 @381

A ver, veo que tienen relación las posiciones que hay entre scores, grupos y marcas, entonces, ¿por qué no haces un hash que tenga 10 llaves, y en cada llave tenga los 3 valores? sería más fácil ordenarlo...

Podría ser un hash de hashes

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
%hash = (uno => {score => 1m grupos => A, marcas => X}, dos => {}, tres => {});
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Para ordenarlo deberías de usar algo así como

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
foreach my $llave (sort {$hash{$a}{'scores'} <= $hash{$b}{'scores'}} keys %hash){
    print "$llave = $hash{$llave}\n";
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


No probé el código pero estoy casi seguro que te lo ordenará por los valores de los scores de cada hash...

Saludos
MonoSwim
Perl Programming Language
Avatar de Usuario
monoswim
Perlero nuevo
Perlero nuevo
 
Mensajes: 452
Registrado: 2003-11-18 16:13 @717
Ubicación: Buenos Aires

Notapor explorer » 2008-06-13 09:51 @452

Lo más inmediato es pensar en usar la función sort(), pero como los datos están algo 'separados', tenemos que crear algún artificio para, primero unirles y luego dejarlo como estaba.

Una primera solución es como la que comenta monoswim: crear una estructura más fácil de ordenar que la que tenemos. Ordenarla y luego dejarlo con la apariencia que tenía originalmente.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use Data::Dumper::Names;
use strict;
use warnings;

my %hash = (
    scores => [qw(1 3 1 2 3 3 1 5 2 3)],
    grupos => [qw(A G H V E T D A A H)],
    marcas => [qw(X X Y X Y Y Y Y X X)],
);

print Dumper(\%hash);

my @scores = map {
                [
                    $hash{scores}[$_],
                    $hash{grupos}[$_],
                    $hash{marcas}[$_],
                ]
            }
            0 .. $#{$hash{scores}};

print Dumper(\@scores);

my @scores_ordenados =
    map  { $_->[1]             }
    sort { $a->[0] <=> $b->[0] }
    map  { [ $_->[0], $_ ]     }
    @scores;

print Dumper(\@scores_ordenados);

my %hash_ordenado;

foreach my $i ( 0 .. $#scores_ordenados ) {
    push @{$hash_ordenado{scores}}, $scores_ordenados[$i][0];
    push @{$hash_ordenado{grupos}}, $scores_ordenados[$i][1];
    push @{$hash_ordenado{marcas}}, $scores_ordenados[$i][2];
}

print Dumper(\%hash_ordenado);

__END__
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
La salida es:
Código: Seleccionar todo
%hash = (
          'scores' => [
                        '1',
                        '3',
                        '1',
                        '2',
                        '3',
                        '3',
                        '1',
                        '5',
                        '2',
                        '3'
                      ],
          'marcas' => [
                        'X',
                        'X',
                        'Y',
                        'X',
                        'Y',
                        'Y',
                        'Y',
                        'Y',
                        'X',
                        'X'
                      ],
          'grupos' => [
                        'A',
                        'G',
                        'H',
                        'V',
                        'E',
                        'T',
                        'D',
                        'A',
                        'A',
                        'H'
                      ]
        );
@scores = (
            [
              '1',
              'A',
              'X'
            ],
            [
              '3',
              'G',
              'X'
            ],
            [
              '1',
              'H',
              'Y'
            ],
            [
              '2',
              'V',
              'X'
            ],
            [
              '3',
              'E',
              'Y'
            ],
            [
              '3',
              'T',
              'Y'
            ],
            [
              '1',
              'D',
              'Y'
            ],
            [
              '5',
              'A',
              'Y'
            ],
            [
              '2',
              'A',
              'X'
            ],
            [
              '3',
              'H',
              'X'
            ]
          );
@scores_ordenados = (
                      [
                        '1',
                        'A',
                        'X'
                      ],
                      [
                        '1',
                        'H',
                        'Y'
                      ],
                      [
                        '1',
                        'D',
                        'Y'
                      ],
                      [
                        '2',
                        'V',
                        'X'
                      ],
                      [
                        '2',
                        'A',
                        'X'
                      ],
                      [
                        '3',
                        'G',
                        'X'
                      ],
                      [
                        '3',
                        'E',
                        'Y'
                      ],
                      [
                        '3',
                        'T',
                        'Y'
                      ],
                      [
                        '3',
                        'H',
                        'X'
                      ],
                      [
                        '5',
                        'A',
                        'Y'
                      ]
                    );
%hash_ordenado = (
                   'scores' => [
                                 '1',
                                 '1',
                                 '1',
                                 '2',
                                 '2',
                                 '3',
                                 '3',
                                 '3',
                                 '3',
                                 '5'
                               ],
                   'marcas' => [
                                 'X',
                                 'Y',
                                 'Y',
                                 'X',
                                 'X',
                                 'X',
                                 'Y',
                                 'Y',
                                 'X',
                                 'Y'
                               ],
                   'grupos' => [
                                 'A',
                                 'H',
                                 'D',
                                 'V',
                                 'A',
                                 'G',
                                 'E',
                                 'T',
                                 'H',
                                 'A'
                               ]
                 );

Otra forma, más directa y artística, puede ser esta:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use Data::Dumper::Names;
use strict;
use warnings;

my %hash = (
    scores => [qw(1 3 1 2 3 3 1 5 2 3)],
    grupos => [qw(A G H V E T D A A H)],
    marcas => [qw(X X Y X Y Y Y Y X X)],
);

my %hash_ordenado;
foreach my $item (
    sort { $a->[0] <=> $b->[0] }
    map  {
        [
            $hash{scores}[$_],
            [
                $hash{scores}[$_],
                $hash{grupos}[$_],
                $hash{marcas}[$_],
            ]
        ]
    }
    0 .. $#{$hash{scores}}
    )
{
    foreach my $j ( qw(scores grupos marcas) ) {
        push @{$hash_ordenado{$j}}, shift @{$item->[1]};
    }
}

print Dumper(\%hash_ordenado);
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Realmente, las dos soluciones hacen lo mismo, solo que la segunda se ahorra la creación de las variables temporales.

Y finalmente, una solución muy óptima: ordenar los scores recordando la posición en la que estaban. Luego, sacar los elementos del %hash según las nuevas posiciones de los scores ordenados:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use Data::Dumper::Names;
use strict;
use warnings;

my %hash = (
    scores => [qw(1 3 1 2 3 3 1 5 2 3)],
    grupos => [qw(A G H V E T D A A H)],
    marcas => [qw(X X Y X Y Y Y Y X X)],
);

my @indices_ordenados =
    map  { $_->[1]                   }
    sort { $a->[0] <=> $b->[0]       }
    map  { [ $hash{scores}[$_], $_ ] }
    0 .. $#{$hash{scores}};

my %hash_ordenado;
foreach my $i ( 0 .. $#indices_ordenados ) {
    foreach my $j ( qw( scores grupos marcas ) ) {
        $hash_ordenado{$j}[$i] = $hash{$j}[ $indices_ordenados[$i] ];
    }
}

print Dumper(\%hash_ordenado);
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Como se puede observar, en todas las soluciones he empleado una transformada Schwartziana.

Seguro que hay otras formas de hacerlo.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

problemillas...

Notapor xagutxu_perez » 2008-06-14 08:28 @394

Hola:

He estado probando lo que me respondiste, Explorer, y me da un error. He pensado que quizás sea porque no he definido como tú el hash. Yo lo he hecho así:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
        $corpus{"scores"} = [@scores];
        $corpus{"clase"} = [@clases];
        $corpus{"grupo"} = [@grupo];
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


En realidad, es un poco más largo. Supongo que si "lleno" así el hash %corpus, la solución que me propones (yo he estado probando con la tercera) no funcionará, porque la variable $_ no contendrá lo mismo, ¿no?

Puedo cambiar el hash, pero, entonces, ¿cómo haría para introducir los arrays en el hash?

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
         %corpus (
                  scores => @scores,
                  clase => @clases,
                  grupo => @grupo);
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Así no se puede hacer, ¿no?
xagutxu_perez
Perlero nuevo
Perlero nuevo
 
Mensajes: 43
Registrado: 2008-04-04 03:56 @206

Notapor explorer » 2008-06-14 10:50 @493

Prueba esto:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my %hash = (
    scores => [qw(1 3 1 2 3 3 1 5 2 3)],
    grupos => [qw(A G H V E T D A A H)],
    marcas => [qw(X X Y X Y Y Y Y X X)],
);

print Dumper(\%hash);

my @scores = qw(1 3 1 2 3 3 1 5 2 3);
my @grupos = qw(A G H V E T D A A H);
my @marcas = qw(X X Y X Y Y Y Y X X);
my %corpus;
$corpus{scores} = [ @scores ];
$corpus{grupos} = [ @grupos ];
$corpus{marcas} = [ @marcas ];

print Dumper(\%corpus);

%corpus = (
    scores => @scores,
    grupos => @grupos,
    marcas => @marcas,
);

print Dumper(\%corpus);
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
y verás las diferencias:
Código: Seleccionar todo
$VAR1 = {
          'scores' => [
                        '1',
                        '3',
                        '1',
                        '2',
                        '3',
                        '3',
                        '1',
                        '5',
                        '2',
                        '3'
                      ],
          'marcas' => [
                        'X',
                        'X',
                        'Y',
                        'X',
                        'Y',
                        'Y',
                        'Y',
                        'Y',
                        'X',
                        'X'
                      ],
          'grupos' => [
                        'A',
                        'G',
                        'H',
                        'V',
                        'E',
                        'T',
                        'D',
                        'A',
                        'A',
                        'H'
                      ]
        };
$VAR1 = {
          'scores' => [
                        '1',
                        '3',
                        '1',
                        '2',
                        '3',
                        '3',
                        '1',
                        '5',
                        '2',
                        '3'
                      ],
          'marcas' => [
                        'X',
                        'X',
                        'Y',
                        'X',
                        'Y',
                        'Y',
                        'Y',
                        'Y',
                        'X',
                        'X'
                      ],
          'grupos' => [
                        'A',
                        'G',
                        'H',
                        'V',
                        'E',
                        'T',
                        'D',
                        'A',
                        'A',
                        'H'
                      ]
        };
Odd number of elements in hash assignment at ./kk.pl line 24.
$VAR1 = {
          'A' => 'H',
          'scores' => '1',
          'marcas' => 'X',
          'X' => undef,
          '3' => 'grupos',
          'Y' => 'X',
          'E' => 'T',
          '2' => '3',
          'H' => 'V',
          'D' => 'A',
          '5' => '2'
        };
Como ves, las dos primeras asignaciones son idénticas, pero la tercera no, incluso da un error en pantalla.

Recuerda: Data::Dumper es tu amigo.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor xagutxu_perez » 2008-06-14 13:04 @586

Claro, pero entonces, ¿cómo hago para solucionarlo? La variable "$_" será diferente si completo el hash así,

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my %hash = (
    scores => [qw(1 3 1 2 3 3 1 5 2 3)],
    grupos => [qw(A G H V E T D A A H)],
    marcas => [qw(X X Y X Y Y Y Y X X)],
);
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


o así:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my @scores = qw(1 3 1 2 3 3 1 5 2 3);
my @grupos = qw(A G H V E T D A A H);
my @marcas = qw(X X Y X Y Y Y Y X X);
my %corpus;
$corpus{scores} = [ @scores ];
$corpus{grupos} = [ @grupos ];
$corpus{marcas} = [ @marcas ];
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


¿Si defino el hash como el último caso, también es válido el código que escribiste?

Gracias otra vez,

Xagutxu
xagutxu_perez
Perlero nuevo
Perlero nuevo
 
Mensajes: 43
Registrado: 2008-04-04 03:56 @206

Notapor explorer » 2008-06-14 15:20 @681

No, la variable $_ depende de cada contexto. Es la variable "por defecto" dentro de contextos donde normalmente tendríamos que usar una variable.

Ejemplo. Tenemos un bucle así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
foreach my $i ( @scores ) {
    print $i;
    $i++;     # Incrementar el elemento
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Un ejemplo para ejecutar en línea:
Código: Seleccionar todo
explorer@next3:~> perl -le '@x=(1,2,3,4); foreach $i (@x) { print $i; $i++ } print "@x"'
1
2
3
4
2 3 4 5

En bucles tan sencillos, no es necesario el uso de una variable para realizar algo sencillo. En esos casos se puede sustituir por la variable $_, que en el caso de un bucle, adopta el 'valor' de cada elemento del array:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
foreach ( @scores ) {
    print;
    $_++;     # Incrementar el elemento
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Este código hace lo mismo que antes. Observa que en el print no hemos indicado ninguna variable, por lo que print usará la variable $_.

Después del bucle, $_ pierde el significado que tenía dentro.

Así que ya sabes: $_ no tiene NADA que ver con la definición y declaración de otras variables.

P.D. Lo de 'valor' entre comillas, quiere decir que, realmente, no guarda el valor de cada elemento, sino un 'alias'. Por eso, podemos hacer el incremento de los valores del array, de forma directa.

Más información en perlvar
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 2 invitados

cron