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

Hi I need to sort an array either forward or reverse, depending on a variable. Here is the code I wish would work, but sadly fails. Any ideas how to simply write this.
my $sorter; if ($something){$sorter='reverse';} foreach ($sorter sort @array ) { blah; }
So that if $something, it will sort the array in reverse, otherwise $sorter doesn't exist and it will sort the array default. Basically I want to use a variable as perl syntax.

Replies are listed 'Best First'.
Re: Reverse sort array
by ikegami (Patriarch) on Aug 19, 2009 at 22:22 UTC
    sort allows for a custom compare function.
    my $sorter = ( $reverse ? sub { $b cmp $a } : sub { $a cmp $b } ); for (sort $sorter @array) { ... }
      Thank you for your reply. I am sorry but it is not immediately clear to me how to use this code you have written.
        my $reverse = its_reverse_sorting(time); # set or clear flag my $sorter = ( $reverse ? sub { $b cmp $a } : sub { $a cmp $b } ); for (sort $sorter @array) { ... }
        I renamed $something to $reverse, but it's otherwise a drop-in replacement for the code you posted.

      Or, if you want to do it without ?: and add a slight touch of obfuscation:

      for (sort { "-"x!!$reverse.1*($a cmp $b) } @array) { ... }

      -- 
      Ronald Fischer <ynnor@mm.st>
        If I wanted to avoid the conditional operator, it would be because I had many options. In that case, I would use a lookup table.
        my $sorter = $lkup{$method}{$direction}; for (sort $sorter @array) { ... }
Re: Reverse sort array
by Tanktalus (Canon) on Aug 19, 2009 at 22:29 UTC

    Why not just do the reverse when requested?

    my @ordered = sort @array; @ordered = reverse @array if $something;
    You could use eval STRING, but that's evil, so let's avoid that. Another possibility is:
    foreach ($something ? reverse sort @array : sort @array)
    but I don't really like that - too much copying (especially if you have a sort function). Another one that is close to being nice is:
    use Sort::Key qw(keysort rkeysort); my $sorter = $something ? \&rkeysort : \&keysort; foreach ($sorter->(sub { $_ }, @array) )
    (Untested - you may need to pass in \@array.) Along these lines, you could use the multikeysorter from Sort::Key) to create a sorter sub that sorts in the correct order - you pass in something based on the $something.

      thank you
      foreach ($something ? reverse sort @array : sort @array)
      worked well for me.
Re: Reverse sort array
by AnomalousMonk (Archbishop) on Aug 19, 2009 at 22:34 UTC
    A similar approach, but likely slower due to more levels of indirection:
    >perl -wMstrict -le "sub ascending { $a cmp $b } sub descending { - ascending() } my @ra = qw(q w e r t y u i o p); my $sorter = 'descending'; @ra = sort $sorter @ra; print qq{@ra}; $sorter = 'ascending'; @ra = sort $sorter @ra; print qq{@ra}; " y w u t r q p o i e e i o p q r t u w y

      You can get by with just one sorting routine by reversing the arguments as needed.

      $ perl -Mstrict -wle ' > my @arr = qw{ q w e r t y u i o p }; > my $sc = sub { my( $a, $b ) = @_; $a cmp $b }; > my $rev = 0; > my @asc = sort { $rev ? $sc->( $b, $a ) : $sc->( $a, $b) } @arr; > $rev = 1; > my @des = sort { $rev ? $sc->( $b, $a ) : $sc->( $a, $b) } @arr; > print qq{@arr\n@asc\n@des};' q w e r t y u i o p e i o p q r t u w y y w u t r q p o i e $

      I hope this is of interest.

      Cheers,

      JohnGG