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

Hi Monks.

I imagine that there is a good, accepted way to do this, but it seems that I can't find the right words to bash in to google.

Copying from the O'REILLY "Programming Perl" book (and simplifying a bit), I have, for example :

sub prospects { $b->{SALARY} <=> $a->{SALARY} || $b->{HEIGHT} <=> $a->{HEIGHT} || $b->{AGE} <=> $a->{AGE} } @sorted = sort prospects @recs

Now, I inderstand that, but I wand to be able to sort on different fields, and potentially a different number of fields. I'd like to be able to pass the keys to be sorted on to a sub somehow. I could do it with, e.g., maximum 10 fields allowed for, but I would like it to work with any number of fields. I can't work out how to do it without an eval, which I'd like to avoid.

Ideally I would like to be able to use different sort operators / subs with each key, perhaps by passing my function an array something like :

[ { KeyName => 'SALARY', SortType => '<=>' }, { KeyName => 'FIRST_NAME', SortType => 'cmp' } ]

So I basically want to make a function that is used like :

@sorted_hash_ref = flexible_sort( \@unsorted_hash_refs, \@sort_config );

Apologies for poor terminology etc. Thanks in advance!

Replies are listed 'Best First'.
Re: sort by variable, configurable fields
by AnomalousMonk (Archbishop) on Feb 15, 2010 at 19:11 UTC
Re: sort by variable, configurable fields
by Cybris (Sexton) on Feb 15, 2010 at 18:30 UTC

    Unfortunately Perl has no reduce operator yet, so you will have to construct a for loop in your sort function and aggregate the results youself (or break from the loop when the result is clear).

    You can't use strings as operators without eval, but you can use code references, e.g.:

    [ { KeyName => 'SALARY', SortType => sub { shift <=> shift }, }, { KeyName => 'FIRST_NAME', SortType => sub { shift cmp shift }, } ]

    You can then call your code references in the for loop

      ... Perl has no reduce operator ...

      But check out  reduce in List::Util.

Re: sort by variable, configurable fields
by Jenda (Abbot) on Feb 15, 2010 at 21:18 UTC

    Well you could use function composition to build the sort routine, but as function calls are fairly expensive in Perl, it would be slow. I do think an eval"" IS the best solution. You just have to make sure all the stuff you interpolate into the code you build is safe.

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

Re: sort by variable, configurable fields
by salva (Canon) on Feb 16, 2010 at 11:42 UTC
    use Sort::Key:
    use Sort::Key qw(multikeysorter); my @spec = ( { KeyName => 'SALARY', SortType => 'number' }, { KeyName => 'FIRST_NAME', SortType => 'string' } ); my @keys = map $_->{KeyName} @spec; my @types = map $_->{SortType} @spec; my $sorter = multikeysorter(sub { @{$_}{@keys} }, @types); my @sorted_data = $sorter->(@data);
Re: sort by variable, configurable fields
by DrHyde (Prior) on Feb 16, 2010 at 11:27 UTC

    Sort::MultipleFields

    You may also find the code educational as a use of closures - it was in fact written primarily as an example of their use.