Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I want to sort a hash's %h keys in an arbitrary order @seq. I can achieve that by building a helper hash %seq_helper in which the first key is the first item in @seq and the first value is 0, the second key is the next item in @seq and the second value is 1 and so on. Then I can easily compare the items of @seq using <=> and the values of %seq_helper.

However, it does not seem to be possible to make this into a sorting subroutine that accepts a list that tells the sub how I want the items to be sorted. The following is a syntax error after sort by_seq.

use strict; use diagnostics; sub by_seq { my $seq_aref = shift; my %seq_helper; { my $index = 0; for my $item (@{ $seq_aref }) { $seq_helper{$item} = $index; $index++; }; }; return $seq_helper{$a} <=> $seq_helper{$b}; }; # -- my %h = (one=>2,three=>4,five=>6,seven=>8,nine=>0); for (sort by_seq([qw(five nine one seven three)]) keys %h) { print "$_: $h{$_}\n"; }; __DATA__ expected output five: 6 nine: 0 one: 2 seven: 8 three: 4

➀ Why?

➁ How would you solve it?

Replies are listed 'Best First'.
Re: sort in arbitrary order with subroutine, syntax error
by Corion (Patriarch) on Dec 19, 2007 at 10:56 UTC

    The code block given to sort will not get any arguments (or, if it gets them, the arguments will be $a and $b. If you're not passing a code block as the first argument, you need to list all arguments separated by commas:

    for (sort by_seq([qw(five nine one seven three)]), keys %h) {

    (except that by_seq won't do what you want yet).

    If you want to make the thing work, the easiest way is to generate the comparison subroutine on the fly and use it with sort:

    use strict; #use diagnostics; sub by_seq { my $seq_aref = shift; my %seq_helper; { my $index = 0; for my $item (@{ $seq_aref }) { $seq_helper{$item} = $index; $index++; }; }; # Here I return a subroutine that takes two arguments # (the left and the right element to be compared) return sub { $seq_helper{$_[0]} <=> $seq_helper{$_[1]} }; }; # -- my %h = (one=>2,three=>4,five=>6,seven=>8,nine=>0); # Create our custom comparison routine my $sorter = by_seq([qw(five nine one seven three)]); for (sort {$sorter->($a,$b)}, keys %h) { print "$_: $h{$_}\n"; };

    I thought I could write sort $sorter, LIST, but that gets interpreted by Perl (rightfully) as sort LIST, so I guess you'll be stuck with the extra code block.

Re: sort in arbitrary order with subroutine, syntax error
by moritz (Cardinal) on Dec 19, 2007 at 10:45 UTC
    The sort docu tell you that can either use sort SUBNAME LIST or sort BLOCK LIST, and you do neither => syntax error.

    Just use the BLOCK form, and everything will work: sort {by_seq([qw(five nine one seven three)])} keys %h

Re: sort in arbitrary order with subroutine, syntax error
by citromatik (Curate) on Dec 19, 2007 at 10:49 UTC

    If I understood well, you don't need to sort nothing, you can use the keys directly: a simple hash slice can do the job:

    my %h = (one=>2,three=>4,five=>6,seven=>8,nine=>0); print "$_\n" for (@h{qw/five nine one seven three/})'

    Outputs:

    6 0 2 8 4

    Or even easier:

    my %h = (one=>2,three=>4,five=>6,seven=>8,nine=>0); print "$_ => $h{$_}\n" for (qw/five nine one seven three/);

    Outputs:

    five => 6 nine => 0 one => 2 seven => 8 three => 4

    Is this what you are trying to do?

    citromatik