Array#sort
.sort and .sort_by arrange array elements in order. Use these when you need to organize data for display, search, or further processing. Both methods return a new array, leaving the original untouched.
Syntax
array.sort
array.sort { |a, b| block }
array.sort_by
array.sort_by { |element| block }
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| block | Block | See description | For .sort: compares two elements (a, b). For .sort_by: returns the sort key for each element. Without a block, elements are compared using the <=> operator. |
.sort
Basic Usage
Without a block, .sort compares elements using the <=> operator (spaceship). This works for numbers and strings because they define this method:
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
numbers.sort
# => [1, 1, 2, 3, 4, 5, 6, 9]
words = ["banana", "apple", "cherry"]
words.sort
# => ["apple", "banana", "cherry"]
Custom Order with a Block
The block receives two elements and should return:
- A negative number when the first should come first
- Zero when they’re equal (order is undefined)
- A positive number when the second should come first
# Reverse order
[3, 1, 2].sort { |a, b| b <=> a }
# => [3, 2, 1]
# Sort by absolute value
[-5, -3, 1, 2].sort { |a, b| a.abs <=> b.abs }
# => [1, 2, -3, -5]
The <=> shorthand makes this cleaner:
# Ascending (default)
[3, 1, 2].sort { |a, b| a <=> b }
# => [1, 2, 3]
# Descending
[3, 1, 2].sort { |a, b| b <=> a }
# => [3, 2, 1]
.sort_by
Basic Usage
.sort_by takes a block that returns a value to sort by. This is often simpler than writing custom comparison logic:
words = ["apple", "banana", "cherry", "date"]
# Sort by length
words.sort_by(&:length)
# => ["date", "apple", "banana", "cherry"]
# Equivalent to:
words.sort_by { |w| w.length }
# => ["date", "apple", "banana", "cherry"]
When .sort_by Shines
The key advantage of .sort_by is efficiency when the sort key is expensive to compute. It evaluates the block once per element (caching the result), whereas .sort may call the block many times during comparison.
files = ["report.pdf", "script.rb", "data.json", "readme.md"]
# Sort by file extension - simple and readable
files.sort_by { |f| File.extname(f) }
# => [".json", ".md", ".pdf", ".rb"]
# Sort by length, reversed
files.sort_by { |f| -f.length }
# => ["readme.md", "report.pdf", "script.rb", "data.json"]
Sorting Hashes
prices = { apple: 1.50, banana: 0.75, orange: 2.00 }
# Sort by value
prices.sort_by { |_name, price| price }
# => [[:banana, 0.75], [:apple, 1.50], [:orange, 2.00]]
# Sort by key (symbol name)
prices.sort_by { |name, _price| name.to_s }
# => [:apple, :banana, :orange]
Performance Comparison
For simple comparisons, .sort is faster. For complex keys, .sort_by wins because it computes each key once:
# .sort - compares many times
large_array.sort { |a, b| a[:expensive_field] <=> b[:expensive_field] }
# .sort_by - computes key once per element
large_array.sort_by { |e| e[:expensive_field] }
In practice, the readability of .sort_by usually outweighs the minor performance difference unless you’re sorting millions of elements.
Gotchas
Unstable Sort
Both methods produce unstable sorts. When two elements are equal (block returns zero), their relative order is not guaranteed:
# May return [1, 1, 2] or [1, 1, 2] - either is valid
[1, 1, 2].sort_by { |x| x }
Missing Comparison Method
Elements must define <=> or you must provide a block:
[Object.new, Object.new].sort
# => ArgumentError: comparison of Object with Object failed
# Works with a block
[Object.new, Object.new].sort { |a, b| a.object_id <=> b.object_id }
# Works - sorts by object_id
Symbol#to_proc Only Works for Methods
You can’t pass arguments to the method when using &:method:
# This works - length takes no arguments
words.sort_by(&:length)
# This does NOT work - to_i takes an argument
["1", "2"].sort_by(&:to_i)
# Use a block instead:
["1", "2"].sort_by { |s| s.to_i }
See Also
.max / .min— Find extreme values (often used with sorted data).sample— Get random elements (useful for shuffling).map— Transform elements (often combined with sort_by)