Custom array sort in perl
I have a perl array of to-do tasks开发者_JAVA技巧 that looks like this:
@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"
);
That first number is the task's ID. If a task has ([A-Z]) next to, that defines the task's priority. What I want to do is sort the tasks array in a way that places the prioritized items first (and in order of descending priority, from 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",
);
I cannot use a regular sort()
because of those IDs next to the tasks, so I'm assuming that some sort of customized sorting subroutine is needed. However, my knowledge of how to do this efficiently in perl is minimal.
Thanks, all.
Sounds like you want the Schwartzian transform:
@todos =
map { $_->[0] }
sort { $a->[1] cmp $b->[1] or $a->[0] cmp $b->[0] }
map { [ $_, /^\d+ \(([[:alpha:]])\)/ ? $1 : "[" ] }
@todos;
"[" is the character after "Z"; giving this "priority" to otherwise unprioritized items will sort them after the prioritized items.
Alternately, and perhaps more easily graspable:
@todos =
map { substr $_, 1 }
sort
map { (/^\d+ \(([[:alpha:]])\)/ ? $1 : "[") . $_ }
@todos;
Here's a version that is fairly explicit about how it works:
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;
Here's fixed @Sean's solution that uses numerical sort for task IDs (thus 10th task goes after 9th as it should):
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;
use Sort::Key 'keysort';
my @sorted = keysort { /^\d+\s+\(([A-Z])\)/ ? $1 : 'ZZ' } @todos;
Much simpler solution:
sort {($a =~ /\((.)\)/)[0] cmp ($b =~ /\((.)\)/)[0]} @todos;
精彩评论