Notes on sorting collections in Ruby.
sort method
numbers = [1 , 3, 5, 2, 4]
p numbers.sort
Defining <=>
The spaceship operator (<=>) was covered in Literal constructors, built-in operators and conversion methods but it can also be used to define the sorting criteria.
class Person
attr_accessor :name, :age, :country
def initialize(name, age, country)
self.name = name
self.age = age
self.country = country
end
def <=>(other_person)
self.age <=> other_person.age
end
def to_s
self.name
end
end
p1 = Person.new("Bill", 20, "UK")
p2 = Person.new("Bob", 30, "France")
p3 = Person.new("Ben", 50, "France")
people = [p2, p3, p1]
p people
p people.sort
Output:
[Bob, Ben, Bill]
[Bill, Bob, Ben]
Mixing Comparable
Other type of comparison (<, >, ==, etc.) can be performed by mixing the Comparable module:
class Person
include Comparable
attr_accessor :name, :age, :country
def initialize(name, age, country)
self.name = name
self.age = age
self.country = country
end
def <=>(other_person)
self.age <=> other_person.age
end
def to_s
"#{self.name}:#{self.age}"
end
end
p1 = Person.new("Bill", 20, "UK")
p2 = Person.new("Bob", 30, "France")
p3 = Person.new("Ben", 50, "France")
people = [p2, p3, p1]
p people.sort
puts p1.eql?(p2)
puts p1.equal?(p2)
puts p1 == p2
puts p1 < p2
puts p1 >= p2
Code block as sorting criteria
class Person
attr_accessor :name, :age, :country
def initialize(name, age, country)
self.name = name
self.age = age
self.country = country
end
def <=>(other_person)
self.age <=> other_person.age
end
def to_s
"#{self.name}:#{self.age}"
end
end
p1 = Person.new("Bill", 20, "UK")
p2 = Person.new("Bob", 30, "France")
p3 = Person.new("Ben", 50, "France")
people = [p2, p3, p1]
p people
p people.sort
# Equivalent:
# people.sort {|first, second| first.name <=> second.name}
sorted = people.sort do |first, second|
first.name <=> second.name
end
p sorted
Output:
[Bob:30, Ben:50, Bill:20]
[Bill:20, Bob:30, Ben:50]
[Ben:50, Bill:20, Bob:30]
sort_by
The sorb_by method is similar to calling the sort method with a code block but more ‘sugary’:
class Person
attr_accessor :name, :age, :country
def initialize(name, age, country)
self.name = name
self.age = age
self.country = country
end
def <=>(other_person)
self.age <=> other_person.age
end
def to_s
"#{self.name}:#{self.age}"
end
end
p1 = Person.new("Bill", 20, "UK")
p2 = Person.new("Bob", 30, "France")
p3 = Person.new("Ben", 50, "France")
people = [p2, p3, p1]
p people.sort_by {|person| person.name}