Ruby's Enumerable sort_by

by Derek Sivers

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



I love when I come across something that's SO much easier to do in one language than another.

Here's something that I wrote a whole program to do in PHP once, that I realized is built right into Ruby:

Say you've got an array of any complex things (objects or hashes most likely), and you want to sort the array by one of the attributes of the things.

Ruby has this wonderful method sort_by that works on any Enumerable lists. See the man page, here.


# make an Array of kids
kids = Array.new
# each kid is a Hash
kids << {'name'=>'Cliff', 'age'=>10}
kids << {'name'=>'Bill', 'age'=>6}
kids << {'name'=>'Andy', 'age'=>13}
#
# the kids sorted by name
kids.sort_by { |k| k['name'] }
# the kids sorted by age
kids.sort_by { |k| k['age'] }

9 Comments

RobbyRussell
2005-03-13 21:39:11
in php
Yeah, having that functionality ready to use on an array is nice in Ruby.


However, in PHP, you can do this with:



$kids = array(); // new array


$kids[] = array('name' => 'Cliff', 'age' => 10);
$kids[] = array('name' => 'Bill', 'age' => 6);
$kids[] = array('name' => 'Andy', 'age' => 13);


print_r($kids);


array_multisort($kids, SORT_ASC);


print_r($kids);


Which outputs:



Array
(
[0] => Array
(
[name] => Cliff
[age] => 10
)


[1] => Array
(
[name] => Bill
[age] => 6
)


[2] => Array
(
[name] => Andy
[age] => 13
)


)
Array
(
[0] => Array
(
[name] => Andy
[age] => 13
)


[1] => Array
(
[name] => Bill
[age] => 6
)


[2] => Array
(
[name] => Cliff
[age] => 10
)


)


..just don't want people to think that you can't do it easily in PHP too. ;-)


RobbyRussell
2005-03-13 21:42:02
in php
Oops, missed the second sort_by 'age'.


Yeah, PHP doesn't make this as easy. *sigh*


simon_hibbs
2005-03-14 08:29:36
Is there anything like this in Python?
I'm just starting to learn Python. This looks like a useful feature, does anyone know if there's an easy way to do this in The Py? I've had a poke around i the documentation, but can't find anything yet.


Simon

sklar
2005-03-14 10:12:58
It actually is pretty easy in PHP...
The usort() function does make it pretty easy to do this in PHP:

$kids[] = array('name' => 'Cliff', 'age' => 10);
$kids[] = array('name' => 'Bill', 'age' => 6);
$kids[] = array('name' => 'Andy', 'age' => 13);



// sort by name
function by_name($a, $b) { return ($a['name'] > $b['name']); }
usort($kids, 'by_name');



// sort by age
function by_age($a, $b) { return ($a['age'] > $b['age']); }
usort($kids, 'by_age');


Alper
2005-03-14 13:34:09
Is there anything like this in Python?
You should really read this: http://www.amk.ca/python/howto/sorting/sorting.html


It would become something like:



a = []


a.append({'name': 'Cliff', 'age': 10})
a.append({'name': 'Bill', 'age': 6})
a.append({'name': 'Andy', 'age': 13})


a.sort(lambda x, y: x['age']-y['age'])
a.sort(lambda x, y: cmp(x['name'], y['name']))

riffraff
2005-03-14 14:00:09
It actually is pretty easy in PHP...
this is exactly what the ruby's Enumerable#sort() method does. Except ruby has blocks, so you don't have to define a function, you just inline the code, and it works for every data structure that defines each be it an array or tree or hash or whatever.


The point of using sort_by is that it implicitly uses the so-called shwartzian transform, or sometimes named DecorateSortUndecorate.
This way, it does not need to generate two ne objects for each call, and the result is that this is much faster.
I guess you could write a DSU-sort in php, anyway, you just need something like map().

riffraff
2005-03-14 14:05:25
Is there anything like this in Python?
n ot really, as in the php case in the former comment, this is not what sort_by() does in ruby. You can see there are two arguments in this python version, and just one in the ruby one. This basically mean that for each iteration you build two new objects, and this is quite a bit slower.


The ruby version of the two-arguments version is sort().
AFAIK there is a way to use a one-parameter function that does a shwartzian transform/DSU in python, since the latest version (2.4) using something like:

a.sort(key=lambda x: x['age'])

(if I recall correct)

stottmj
2005-03-14 18:36:21
in php
As easy in PHP, Python, etc. Well not really...


Ruby is a bit more elegant about it and once you understand
Ruby Blocks and Iterators you find this sort of thing happens in every single data type. There is little need for the FOR loop in Ruby (it's there if you really need it) but blocks are so much cleaner and readable.


Sure, the same functionality as the sort_by is available in other languages but I still don't find it as clean nor pure as it is in Ruby. There's a whole lot more then 'sort_by' to Ruby.


I hear that Ruby 2.x will be more like Python in that it will be compilable byte-code. Right now, it's simply interpreted.

gnustavo
2005-03-15 17:49:30
How about in Perl?

# make an Array of kids
# each kid is a Hash
@kids = [
{name=>'Cliff', age=>10},
{name=>'Bill', age=>6},
{name=>'Andy', age=>13},
];


# the kids sorted by name
sort {$a->{name} cmp $b->{name}} @kids;
# the kids sorted by age
sort {$a->{age} <=> $b->{age}} @kids;


It seems that it's just as easy in any of the leading scripting languages. As for readability, I guess it's in the eye of the beholder, just like beauty.