Andrea T.
Andrea T.

Reputation: 950

Programmatically change sorting order in Perl

I'd like to give the user the possibility to change the sorting order (asc / desc) in a data structure. As far as I know, this is done changing the order of $a and $bin the code, but I'd like to programmatically change this to avoid redundant code.

I made a working example:

use 5.018;
use warnings;

# Supply any argument to change sorting order
my $sorting_direction = $ARGV[0];

my $data = {
          'item1' => {
                         'min'  => 4,
                         'size' => 825,
                         'max'  => 256,
                       },
          'item2' => {
                         'min'  => 4,
                         'size' => 130,
                         'max'  => 65,
                       },
        };


if (defined $sorting_direction) {
    foreach my $item (sort { $$data{$a}{'size'} <=> $$data{$b}{'size'} } keys %{$data} ) {
        say "$item\t", $$data{$item}{'size'};
    }
} else {
    foreach my $item (sort { $$data{$b}{'size'} <=> $$data{$a}{'size'} } keys %{$data} ) {
        say "$item\t", $$data{$item}{'size'};
    }   
}

Giving any parameter will change the sorting_direction. Can I do this without the if conditional?

Upvotes: 4

Views: 273

Answers (3)

ikegami
ikegami

Reputation: 385655

A generic solution is to use different compare functions.

my %sorters = (
   by_size_asc  => sub { $data->{$a}{size} <=> $data->{$b}{size} },
   by_size_desc => sub { $data->{$b}{size} <=> $data->{$a}{size} },
   # ...
);

@ARGV
   or die("usage\n");

my $sorter = $sorters{$ARGV[0]}
   or die("Invalid sort function \"$ARGV[0]\".\n");

my @sorted_keys = sort $sorter keys(%$data);

You could also use different sort functions, such as when using the great Sort::Key module.

use Sort::Key qw( ikeysort rikeysort );

my %sorters = (
   by_size_asc  => sub { ikeysort  { $data->{$_}{size} } @_ },
   by_size_desc => sub { rikeysort { $data->{$_}{size} } @_ },
   # ...
);

@ARGV
   or die("usage\n");

my $sorter = $sorters{$ARGV[0]}
   or die("Invalid sort function \"$ARGV[0]\".\n");

my @sorted_keys = $sorter->( keys(%$data) );

Upvotes: 7

Grinnz
Grinnz

Reputation: 9231

While it's always going to be slower because it's a full extra operation, if performance is not as much a concern as code cleanliness you could just reverse the list when the opposite sorting direction is chosen. Note that this would be slightly different in the case of sorting equal elements, as sort in Perl is normally stable (equal elements stay in the same order they originally were).

my @sorted = sort { $$data{$a}{'size'} <=> $$data{$b}{'size'} } keys %{$data};
@sorted = reverse @sorted if $reverse;

Upvotes: 1

Skeeve
Skeeve

Reputation: 8202

As <=> has a value of -1, 0 or 1, you can multiply with -1 to get the opposite sorting order.

So if your $sorting_direction is 1 or -1 use

$sorting_direction * ( $$data{$a}{'size'} <=> $$data{$b}{'size'} )

Upvotes: 8

Related Questions