FAQ 4.52 How do I sort an array by (anything)?

FAQ 4.52 How do I sort an array by (anything)?

am 30.09.2007 21:03:02 von PerlFAQ Server

This is an excerpt from the latest version perlfaq4.pod, which
comes with the standard Perl distribution. These postings aim to
reduce the number of repeated questions as well as allow the community
to review and update the answers. The latest version of the complete
perlfaq is at http://faq.perl.org .

------------------------------------------------------------ --------

4.52: How do I sort an array by (anything)?

Supply a comparison function to sort() (described in "sort" in
perlfunc):

@list = sort { $a <=> $b } @list;

The default sort function is cmp, string comparison, which would sort
"(1, 2, 10)" into "(1, 10, 2)". "<=>", used above, is the numerical
comparison operator.

If you have a complicated function needed to pull out the part you want
to sort on, then don't do it inside the sort function. Pull it out
first, because the sort BLOCK can be called many times for the same
element. Here's an example of how to pull out the first word after the
first number on each item, and then sort those words case-insensitively.

@idx = ();
for (@data) {
($item) = /\d+\s*(\S+)/;
push @idx, uc($item);
}
@sorted = @data[ sort { $idx[$a] cmp $idx[$b] } 0 .. $#idx ];

which could also be written this way, using a trick that's come to be
known as the Schwartzian Transform:

@sorted = map { $_->[0] }
sort { $a->[1] cmp $b->[1] }
map { [ $_, uc( (/\d+\s*(\S+)/)[0]) ] } @data;

If you need to sort on several fields, the following paradigm is useful.

@sorted = sort {
field1($a) <=> field1($b) ||
field2($a) cmp field2($b) ||
field3($a) cmp field3($b)
} @data;

This can be conveniently combined with precalculation of keys as given
above.

See the sort article in the "Far More Than You Ever Wanted To Know"
collection in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz for more
about this approach.

See also the question later in perlfaq4 on sorting hashes.



------------------------------------------------------------ --------

The perlfaq-workers, a group of volunteers, maintain the perlfaq. They
are not necessarily experts in every domain where Perl might show up,
so please include as much information as possible and relevant in any
corrections. The perlfaq-workers also don't have access to every
operating system or platform, so please include relevant details for
corrections to examples that do not work on particular platforms.
Working code is greatly appreciated.

If you'd like to help maintain the perlfaq, see the details in
perlfaq.pod.

Re: FAQ 4.52 How do I sort an array by (anything)?

am 02.10.2007 03:46:32 von William James

On Sep 30, 2:03 pm, PerlFAQ Server wrote:

> 4.52: How do I sort an array by (anything)?
[...]
> which could also be written this way, using a trick that's come to be
> known as the Schwartzian Transform:
>
> @sorted = map { $_->[0] }
> sort { $a->[1] cmp $b->[1] }
> map { [ $_, uc( (/\d+\s*(\S+)/)[0]) ] } @data;
>

Ruby:

sorted = data.sort_by{|s| s[ /\d+\s*(\S+)/, 1 ].upcase }

Re: FAQ 4.52 How do I sort an array by (anything)?

am 02.10.2007 04:36:57 von Jim Cochrane

On 2007-10-02, William James wrote:
> On Sep 30, 2:03 pm, PerlFAQ Server wrote:
>
>> 4.52: How do I sort an array by (anything)?
> [...]
>> which could also be written this way, using a trick that's come to be
>> known as the Schwartzian Transform:
>>
>> @sorted = map { $_->[0] }
>> sort { $a->[1] cmp $b->[1] }
>> map { [ $_, uc( (/\d+\s*(\S+)/)[0]) ] } @data;
>>
>
> Ruby:
>
> sorted = data.sort_by{|s| s[ /\d+\s*(\S+)/, 1 ].upcase }
>

Oh, a wiseguy, eh? (Nyuk Nyuk Nyuk!) [
http://www.soitenlystooges.com/item.asp?sku=SGTSKWG
http://en.wikipedia.org/wiki/Three_Stooges#Catchphrases
]

--

Re: FAQ 4.52 How do I sort an array by (anything)?

am 02.10.2007 07:58:36 von Uri Guttman

>>>>> "WJ" == William James writes:

WJ> On Sep 30, 2:03 pm, PerlFAQ Server wrote:
>> 4.52: How do I sort an array by (anything)?
WJ> [...]
>> which could also be written this way, using a trick that's come to be
>> known as the Schwartzian Transform:
>>
>> @sorted = map { $_->[0] }
>> sort { $a->[1] cmp $b->[1] }
>> map { [ $_, uc( (/\d+\s*(\S+)/)[0]) ] } @data;
>>

WJ> Ruby:

WJ> sorted = data.sort_by{|s| s[ /\d+\s*(\S+)/, 1 ].upcase }

i think this is a little clearer and a lot more flexible. also the GRT
is faster than the ST. dunno what ruby does inside that code.



use Sort::Maker ;

my $sorter = make_sorter( 'GRT', 'no_case', string => qr/\d+\s*(\S+)/ ) ;
my @sorted = $sorter->( @data ) ;

uri

--
Uri Guttman ------ uri@stemsystems.com -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org

Re: FAQ 4.52 How do I sort an array by (anything)?

am 03.10.2007 03:31:54 von brian d foy

In article <1191289592.621583.198460@50g2000hsm.googlegroups.com>,
William James wrote:

> On Sep 30, 2:03 pm, PerlFAQ Server wrote:
>
> > 4.52: How do I sort an array by (anything)?
> [...]
> > which could also be written this way, using a trick that's come to be
> > known as the Schwartzian Transform:
> >
> > @sorted = map { $_->[0] }
> > sort { $a->[1] cmp $b->[1] }
> > map { [ $_, uc( (/\d+\s*(\S+)/)[0]) ] } @data;
> >
>
> Ruby:
>
> sorted = data.sort_by{|s| s[ /\d+\s*(\S+)/, 1 ].upcase }

http://www.ruby-doc.org/core/classes/Enumerable.html says:

As of Ruby 1.8, the method Enumerable#sort_by implements a built-in
Schwartzian Transform, useful when key computation or comparison is
expensive..

So, I guess I should say "You're welcome". :)