Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Comparing spaceships (cmp and <=> as options)

by Random_Walk (Parson)
on May 20, 2013 at 15:31 UTC ( #1034352=perlquestion: print w/ replies, xml ) Need Help??
Random_Walk has asked for the wisdom of the Perl Monks concerning the following question:

Fellow Monks, I seek your Perls of wisdom.

I have an array of arrays. the lower level arrays are records with values both numeric and alpha. I want to give the user the option to sort on any of these fields. I thought it would be nice to use a simple hash table referencing the record number to sort on and the comparator to use. I am having a problem using a variable containing comparator. Am I missing something or do I just need another approach?

my $sort = 'hours'; # really comes from a switch my %map = ( # sorting map hours => [0, \sub {<=>}], code => [1, \sub {cmp}], name => [2, \sub {cmp}], ); # example data my @records = ( [10, 'xyz232', 'secret project'], [ 5, 'foo123', 'world domination'], [ 7, 'bar666', 'have a beer'], ); for ( sort {$a->[$map{$sort}->[0]] $map{$sort}->[1] $b->[$map{$sort}->[ +0]]} @records ) { print join ", ", @$_; }

Update

among many variations of syntax I also tried the following. I feel it may be getting closer, other than the fact it won't compile :)
my %map = ( hours => sub {sort { $_[0] <=> $_[0] } };, number => sub {sort { $_[0] <=> $_[0] } };, name => sub {sort { $_[0] <=> $_[0] } };, task => sub {sort { $_[0] <=> $_[0] } };, );

Cheers,
R.

Pereant, qui ante nos nostra dixerunt!

Comment on Comparing spaceships (cmp and <=> as options)
Select or Download Code
Replies are listed 'Best First'.
Re: Comparing spaceships (cmp and <=> as options)
by choroba (Canon) on May 20, 2013 at 15:54 UTC
    sub returns a code reference on its own, no need to add a backslash. You have to add parameters to the subs, though. The canonical ones for sort are $a and $b. Localize them to the value to be sorted on so that the code still returns the full record.
    #!/usr/bin/perl use warnings; use strict; my $sort = 'hours'; # really comes from a switch my %map = ( # sorting map hours => [0, sub {$a <=> $b}], code => [1, sub {$a cmp $b}], name => [2, sub {$a cmp $b}], ); # example data my @records = ( [10, 'xyz232', 'secret project'], [ 5, 'foo123', 'world domination'], [ 7, 'bar666', 'have a beer'], ); for ( sort { local ($a, $b) = map $_->[$map{$sort}->[0]], $a, $b; $map{$sort}[1]->(); } @records ) { print "@$_\n"; }
    لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Comparing spaceships (cmp and <=> as options)
by moritz (Cardinal) on May 20, 2013 at 15:41 UTC
    One problem is that if you write
    \sub {<=>}
    , the <=> doesn't receive any arguments (and doesn't even parse the way you want it to, because the parser expects a term, but finds an operator instead). So you have to write something like

    sub { $_[0] <=> $_[1] }

    instead. This code seems to work:

    #!/usr/bin/perl use strict; use warnings; use 5.010; my $sort = 'hours'; # really comes from a switch my %map = ( # sorting map hours => [0, sub {$_[0] <=> $_[1]}], code => [1, sub {$_[0] cmp $_[1]}], name => [2, sub {$_[0] cmp $_[1]}], ); # example data my @records = ( [10, 'xyz232', 'secret project'], [ 5, 'foo123', 'world domination'], [ 7, 'bar666', 'have a beer'], ); my $elem = $map{$sort}[0]; my $sorter = $map{$sort}[1]; my @sorted = sort { $sorter->($a->[$elem], $b->[$elem]) } @records; say join "\t", @$_ for @sorted;
Re: Comparing spaceships (cmp and <=> as options)
by tobyink (Abbot) on May 20, 2013 at 16:50 UTC

    Have you seen salva's Sort::Key and Sort::Key::Maker - they don't do exactly what you're talking about, but I'm fairly sure you'll find something to like in them.

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: Comparing spaceships (cmp and <=> as options)
by LanX (Canon) on May 20, 2013 at 16:39 UTC
    And now to something completely different =)

    sort allows providing a function name

    sort SUBNAME LIST
    So using an ad-hoc package seems like a short way to solve this :)

    DB<138> sub sort::hours { $a->[0] <=> $b->[0] } DB<139> $sort = 'hours'; => "hours" DB<140> *sort::this=\&{"sort::$sort"} => *sort::this DB<141> sort sort::this @records => ( [5, "foo123", "world domination"], [7, "bar666", "have a beer"], [10, "xyz232", "secret project"], )

    I don't like the glob-manipulation in line 140, but it could be used to eval arbitrary complex sort routines.

    Maybe others have more elegant ideas! =)

    HTH!

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Comparing spaceships (cmp and <=> as options)
by periapt (Hermit) on May 20, 2013 at 17:53 UTC
    Here is a version that uses a variant of the Schwartzian Transform.
    my @records = ( [10, 'xyz232', 'secret project'], [ 5, 'foo123', 'world domination'], [ 7, 'bar666', 'have a beer'], ); my %sort_this_way = ( 'hours' => [0, sub { return sort { $a->[0][$a->[1]] <=> $b->[0][$b->[1]]; } @_; }], 'code' => [1, sub { return sort { $a->[0][$a->[1]] cmp $b->[0][$b->[1]]; } @_; }], 'name' => [2, sub { return sort { $a->[0][$a->[1]] cmp $b->[0][$b->[1]]; } @_; }], ); my $sort = 'hours'; # really comes from a switch my @new = map { $_->[0] } $sort_this_way{$sort}->[1]( map { [ $_ , $sort_this_way{$sort}->[0], $_->[ $sort_this_way{$sort}->[0] ] ] } @records );
    Granted the sort part of the transform is actually a function call but I couldn't remember how to set up a function to not require parenthesis without defining a prototype first.


    PJ
    use strict; use warnings; use diagnostics;

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1034352]
Approved by moritz
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (14)
As of 2015-07-08 05:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (94 votes), past polls