Whenever I hear the word sort in a programming context, I get instant flashbacks from my high-school days. It's like going back in time for a few seconds.
I can't seem to remember when and why I switched from "OMG" to "Oh! Cool!". But anyway, back to sorting, and sorting algorithms.
Just a quick recap of a few sorting algorithms I'm sure you've heard of.
Bubble sort
- Start at the beginning of the list.
- Compare each pair of elements in turn.
- If you need to swap them (i.e. left is bigger than the right), you swap them.
- You repeat the process until there are no more swaps.
One way to optimize bubble sort is to remove the last element from the list at each pass (because it's the largest value).
Merge sort
- You split your list of elements smaller and smaller lists until you get to a list of pairs.
- You sort each pair.
- You merge the pairs in order.
Merge sort works better than bubble sort for larger lists.
Quicksort
- Choose a pivot.
- Put all the numbers smaller than or equal to the pivot to its left.
- Put all the numbers larger than the pivot to its right.
- For each sublist, choose a pivot and repeat the process until there is just one or no elements in each list.
The way you choose the pivot can have a great impact on the operation's performance. Ideally, you want to choose the pivot in the middle of the list.
Working with the Ruby sort method
The Ruby sort
method works by comparing elements of a collection using their <=>
operator (more about that in a second), using the quicksort algorithm.
You can also pass it an optional block if you want to do some custom sorting. The block receives two parameters for you to specify how they should be compared. Let us see an example.
Sorting an array in descending order with sort
Here's how the sort
method works when a block is passed to it.
[1, 2, 3].sort { |a, b| b <=> a } # => [3, 2, 1]
You can probably imagine how that block could easily fit any custom sorting preference.
You can learn more about arrays and see a few more examples on how to sort them in Learn How to Use Ruby Arrays in Less Than 10 Minutes.
The Ruby sorting operator (<=>
)
Also called the spaceship operator, takes two parameters and returns one of three values.
0
if the two parameters are equal.-1
if the first parameter is less than the second parameter.1
if the first parameter is greater than the second parameter.
Note that when the result is 0
, the order of the elements is unpredictable.
Sort vs. sort_by
The difference between sort
and sort_by
is in the parameters to the block.
sort
receives two objects that you have to compare using their <=>
operator.
sort_by
receives just one object, and you have to call a method that will be used to map over the collection. If that sounds confusing, you're not alone. So let us see an example.
If you want to compare sort a two-dimensional array, by the size of each array in the list, you can call :size
on the object the block receives.
matrix = [
[1, 2, 3, 4, 5, 6],
[1, 2, 3],
[1, 2, 3, 4, 5],
[:a, :b, :c, :d]
]
matrix.sort_by { |obj| obj.size }
# => [
[1, 2, 3],
[:a, :b, :c, :d],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5, 6]
]
You can also do multiple sorting, where you sort using multiple values. If for example you want to sort by first and last names, you can do this.
Person = Struct.new(:fname, :lname)
p1 = Person.new("John", "Doe")
p2 = Person.new("Jane", "Doe")
p3 = Person.new("Marry", "Mackan")
p4 = Person.new("John", "Beck")
[p1, p2, p3, p4].sort_by { |p| [p.fname, p.lname] }
# => [
#<struct fname="Jane", lname="Doe">,
#<struct fname="John", lname="Beck">,
#<struct fname="John", lname="Doe">,
#<struct fname="Marry", lname="Mackan">
]
Without a block, sort_by
returns an enumerator.
When you are dealing with larger collections, sort_by
is usually what you want to use.
How to sort a string's characters alphabetically?
The chars
method returns an array of characters. So you can just sort that array, and then join the elements back into a string.
"jhmaeb".chars.sort.join # => "abehjm"
And if you want to do a case insensitive sort, here's how.
"cdBghaZosa".chars.sort(&:casecmp).join # => "aaBcdghosZ"
If you're interested in learning more about how strings work in Ruby, check out How to Use Strings in Ruby.
How to Sort a Hash in Ruby
Often you want to sort a Hash
just like you sort an Array
. A hash is a list of key/value pairs. And it also includes the Enumerable module.
So let us say you want to sort a hash by its keys.
my_hash = {
name: "John",
age: 21,
address: "Main Str. 11",
email: "john@example.com"
}
my_hash.sort_by { |k, v| k }
# => [
[:address, "Main Str. 11"],
[:age, 21],
[:email, "john@example.com"],
[:name, "John"]
]
And if you want to sort it by value, it's just as easy my_hash.sort_by { |k, v| v }
.
As you can see, the result is not a Hash
. It's a two dimensional Array
.
If you want to turn it into a hash, you can call to_h
on it.
How to sort an array of hashes in ruby
If you've done any Rails development, I'm sure you've come across this one.
scores = [
{name: "Superman", score: 745},
{name: "Ironman", score: 9},
{name: "Batman", score: 10}
]
scores.sort_by { |h | h[:name] }
# => [
{:name=>"Batman", :score=>10},
{:name=>"Ironman", :score=>9},
{:name=>"Superman", :score=>745}
]