Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom array sort in perl

I have a perl array of to-do 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"
);

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.

like image 936
ABach Avatar asked May 30 '10 18:05

ABach


People also ask

How do I sort a list in Perl?

sort() function in Perl is used to sort a list with or without the use of method of sorting. This method can be specified by the user in the form of subroutines or blocks. If a subroutine or block is not specified then it will follow the default method of sorting.

How do I sort in ascending order in Perl?

Perl has two operators that behave this way: <=> for sorting numbers in ascending numeric order, and cmp for sorting strings in ascending alphabetic order. By default, sort uses cmp -style comparisons.

How do I sort a value in Perl?

Just use: @sorted = sort { $a <=> $b } @unsorted; The sort function accepts a custom comparison function as its first argument, in the form of a code block.


2 Answers

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;
like image 176
Sean Avatar answered Sep 23 '22 14:09

Sean


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;
like image 21
darch Avatar answered Sep 21 '22 14:09

darch