Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Re: Multiple Sort on selected column

by kennethk (Abbot)
on Oct 18, 2012 at 18:24 UTC ( #999791=note: print w/replies, xml ) Need Help??

in reply to Multiple Sort on selected column

So, in order to implement your desired flexible sort, you need to pass information into your comparison routine. However, you cannot specify arguments to a sub passed to sort directly. There are two straight-forward solutions to this, in my mind. The first would be to use a closure about an array, which I expect would be less maintainable and look more magical under maintenance.

The second, and better option, would be to wrap your comparison routine in an anonymous block, like:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my @employees = ( { FIRST => 'Bill', LAST => 'Gates', SALARY => 600000, AGE => 45 }, { FIRST => 'George', LAST => 'Tester', SALARY => 55000, AGE => 29 }, { FIRST => 'Sally', LAST => 'Developer', SALARY => 55000, AGE => 29 }, { FIRST => 'Joe', LAST => 'Tester', SALARY => 55000, AGE => 29 }, { FIRST => 'Steve', LAST => 'Ballmer', SALARY => 600000, AGE => 41 } ); sub cmp_by { my $result = 0; for my $term (@_) { $result ||= $a->{$term} cmp $b->{$term}; } return $result; } print Dumper sort {cmp_by(qw(LAST FIRST))} @employees;

The way I've written it, you pass an array of keys (see qw in Quote and Quote like Operators in perlop) to your comparison function. The comparison function cycles over that list each time sort calls the block wrapped around it, storing the first time the comparison doesn't yield 0 and returning the result.

Side notes: The language is Perl and the interpreter is perl; there is no PERL. Also, note I've added warnings to my demo code, which can be a real life saver: see Use strict warnings and diagnostics or die.

Update: Note, as thundergnat was kind enough to point out below (and Tanktalus via msg), SALARY and AGE are numeric fields, and should be sorted using the numeric comparison operator, <=>. The code above will sort improperly on numbers that are not the same length (so would appear to succeed on AGE). Of course, as I've noted below, his is not without difficulty...

#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Replies are listed 'Best First'.
Re^2: Multiple Sort on selected column
by thundergnat (Deacon) on Oct 18, 2012 at 19:31 UTC

    I agree with one small caveat. It might be better to change the cmp_by sub to do a numeric comparison in addition to the string comparison. With just the cmp, 600000 sorts after 55000 but 400000 would sort before. The following will sort numerically largest to smallest then alphabetically.

    sub cmp_by { my $result; for my $term (@_) { $result ||= +$b->{$term} <=> +$a->{$term} or $a->{$term} cmp $ +b->{$term}; } return $result; }
      Your point is, of course valid. However, you should test your code before running it. Your or is lower precedence than = (see Operator Precedence and Associativity). This is, for example, why the classic or die construct works so well. This also means your code will never actually sort by string - warnings would have told you that you are performing a comparison in void context. In addition, + is a no-op in unary operator context; you likely meant the numification ("Venus") operator, 0+. This will not suppress the string in numeric context warnings either, since you are still using a string in a numeric context.

      If you want to do auto-detection, you'd be better off using the Conditional Operator using looks_like_number from Scalar::Util:

      sub cmp_by { use Scalar::Util 'looks_like_number'; my $result = 0; for my $term (@_) { $result ||= looks_like_number($a) ? $a->{$term} <=> $b->{$term} : $a->{$term} cmp $b->{$term}; } return $result; }

      #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re^2: Multiple Sort on selected column
by huchister (Acolyte) on Oct 18, 2012 at 18:45 UTC
    Thanks, this was exactly what I've needed.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://999791]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (6)
As of 2018-06-19 02:24 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (111 votes). Check out past polls.