Sorting By Functions in Crystal

Sometimes we’ll want to sort a collection by something other than its natural order. For example, suppose we wanted to sort strings by their length instead of alphabetically. Here’s an example of custom sorts in Crystal.

# We don't need to import any modules for this example in Crystal

# Define our main function
def main
  fruits = ["peach", "banana", "kiwi"]

  # We implement a comparison function for string lengths.
  len_cmp = ->(a : String, b : String) { a.size <=> b.size }

  # Now we can call `sort_by!` with this custom comparison function 
  # to sort `fruits` by name length.
  fruits.sort_by! { |f| f.size }
  puts fruits

  # We can use the same technique to sort an array of 
  # values that aren't built-in types.
  class Person
    property name : String
    property age : Int32

    def initialize(@name, @age)
    end

    def to_s
      "{#{name}, #{age}}"
    end
  end

  people = [
    Person.new("Jax", 37),
    Person.new("TJ", 25),
    Person.new("Alex", 72)
  ]

  # Sort `people` by age using `sort_by!`.
  people.sort_by! { |person| person.age }
  puts people
end

main

In this Crystal version:

  1. We don’t need to import any modules as Crystal’s standard library is available by default.

  2. Instead of slices.SortFunc, we use Crystal’s built-in sort_by! method which sorts the array in-place.

  3. The comparison function is simplified to use Crystal’s spaceship operator <=>.

  4. For sorting custom types, we define a Person class instead of a struct. Crystal uses classes for this kind of structure.

  5. We use Crystal’s property syntax to automatically create getter and setter methods for name and age.

  6. We implement a to_s method for Person to make the output more readable.

  7. The sorting of people is done using the same sort_by! method, specifying the age property to sort by.

When you run this program, you should see output similar to:

["kiwi", "peach", "banana"]
[{TJ, 25}, {Jax, 37}, {Alex, 72}]

This example demonstrates how to perform custom sorting in Crystal, both for built-in types and custom classes.