Custom array sorting in perl

I have an array of perl tasks 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" ); 

This first number is the identifier of the task. If a task has ([AZ]) nearby, this determines the priority of the task. What I want to do is sort the array of tasks in such a way as to put priority elements first (and in decreasing order of priority from A to 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 can't use regular sort() because of these identifiers next to tasks, so I assume some sort of custom sort routine is needed. However, my knowledge of how to do this effectively in perl is minimal.

Thank you all.

+6
sorting arrays perl
source share
5 answers

It looks like you want a Schwartz transform :

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

"[" - the character after the "Z"; giving this "priority" otherwise, unprivileged elements will sort them after the priority items.

Alternatively and perhaps more easily understood:

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

Here's a version that says quite clearly how it works:

 my @sorted_todos = sort { my ($right_prio) = ($b =~ /^\d+\s+\(([AZ])\)/); return -1 unless defined $right_prio; my ($left_prio) = ($a =~ /^\d+\s+\(([AZ])\)/); return 1 unless defined $left_prio; return $left_prio cmp $right_prio; } @todos; 
+3
source share

Here's a fixed @Sean solution that uses numerical sorting for task identifiers (thus, the 10th task comes after the 9th level):

 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; 
0
source share
 use Sort::Key 'keysort'; my @sorted = keysort { /^\d+\s+\(([AZ])\)/ ? $1 : 'ZZ' } @todos; 
0
source share

A simpler solution:

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

All Articles