2010-05-30 8 views
6

que tiene una matriz de Perl tareas a realizar que tiene este aspecto:gama de ordenación personalizada en Perl

@todos = (
    "1 (A) Complete online final @evm4700 t:2010-06-02", 
    "3 Write thank-you t:2010-06-10", 
    "4 (B) Clean t:2010-05-30", 
    "5 Donate to LSF t:2010-06-02", 
    "6 (A) t:2010-05-30 Pick up dry cleaning", 
    "2 (C) Call Chris Johnson t:2010-06-01" 
); 

Ese primer número es el ID de la tarea. Si una tarea tiene ([A-Z]) junto a, eso define la prioridad de la tarea. Lo que quiero hacer es ordenar la matriz tareas de una manera que coloca los elementos priorizados primera (y en orden de prioridad descendente, de la A - Z):

@todos = (
    "1 (A) Complete online final @evm4700 t:2010-06-02", 
    "6 (A) t:2010-05-30 Pick up dry cleaning", 
    "4 (B) Clean t:2010-05-30", 
    "2 (C) Call Chris Johnson t:2010-06-01" 
    "3 Write thank-you t:2010-06-10", 
    "5 Donate to LSF t:2010-06-02", 
); 

no puedo usar un habitual sort() a causa de esas identificaciones al lado de las tareas, entonces supongo que se necesita algún tipo de subrutina de clasificación personalizada. Sin embargo, mi conocimiento de cómo hacer esto de manera eficiente en Perl es mínimo.

Gracias, todo.

Respuesta

12

suena como usted desea que el Schwartzian transform:

@todos = 
    map { $_->[0] } 
    sort { $a->[1] cmp $b->[1] or $a->[0] cmp $b->[0] } 
    map { [ $_, /^\d+ \(([[:alpha:]])\)/ ? $1 : "[" ] } 
    @todos; 

"[" es el carácter después de "Z"; dando esta "prioridad" a los elementos sin priorizar será ordenarlos después de los elementos priorizados.

Alternativamente, y tal vez más fácilmente asible:

@todos = 
    map { substr $_, 1 } 
    sort 
    map { (/^\d+ \(([[:alpha:]])\)/ ? $1 : "[") . $_ } 
    @todos; 
+1

@Sean: Mientras que una transformación de Schwartzian es maravilloso (y aplicable), es muy difícil de seguir, especialmente teniendo en cuenta que el PO es un principiante. – Zaid

+2

@Sean: esta es una solución genial; gracias. ¿Necesita escapar ese ']' ater: alpha :? @Zaid: a pesar de que soy un principiante, @Sean me dio ese enlace que explica la transformación de Schwartz, así que puedo entenderlo. :) – ABach

+0

@ABach: No, no se necesita escaparse; esta es una clase de caracteres POSIX. (Consulte la página de manual de Perlre.) ... Pero hice un error tipográfico ("[: alpha:]]" en lugar de "[[: alpha:]]", que acabo de arreglar. – Sean

0

Aquí es fija @Sean's solution que utiliza una especie numérica para los ID de tarea (por lo tanto 10a tarea va después del 9 de como debería):

my @sorted_todos = map { $_->[0] } 
    sort { $a->[1][1] cmp $b->[1][1] # A 
         || 
      $a->[1][0] <=> $b->[1][0] # 1 
    } map { [ $_, /^(\d+) \(([[:alpha:]])\)/ ? [$1, $2] : [0, "zz"]] } @todos; 
+1

Por algo tan pequeño, la edición de la respuesta de Sean parece suficiente. No estás exactamente carente de representante. – rjh

+1

@rjh: lea los comentarios a la respuesta de Sean, el OP no quiere la solución que proporcioné, por lo que sería incorrecto por mi parte editar la respuesta de Sean. Por otro lado, mi respuesta es útil para otros que buscarían "ordenar de forma personalizada en perl" en el futuro. – jfs

3

He aquí una versión que es bastante explícita acerca de cómo funciona:

my @sorted_todos = sort { 
    my ($right_prio) = ($b =~ /^\d+\s+\(([A-Z])\)/); 
    return -1 unless defined $right_prio; 
    my ($left_prio) = ($a =~ /^\d+\s+\(([A-Z])\)/); 
    return 1 unless defined $left_prio; 
    return $left_prio cmp $right_prio; 
} @todos; 
0
use Sort::Key 'keysort'; 

my @sorted = keysort { /^\d+\s+\(([A-Z])\)/ ? $1 : 'ZZ' } @todos; 
0

solución mucho más sencilla:

sort {($a =~ /\((.)\)/)[0] cmp ($b =~ /\((.)\)/)[0]} @todos;