philiprehberger-natural_sort
Human-friendly natural sorting — "file2" before "file10"
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-natural_sort"
Or install directly:
gem install philiprehberger-natural_sort
Usage
require "philiprehberger/natural_sort"
sorted = Philiprehberger::NaturalSort.sort(["file10", "file2", "file1"])
# => ["file1", "file2", "file10"]
Sorting with a Block
items = [{ name: "img10" }, { name: "img2" }, { name: "img1" }]
sorted = Philiprehberger::NaturalSort.sort_by(items) { |x| x[:name] }
# => [{ name: "img1" }, { name: "img2" }, { name: "img10" }]
Sorting with a Refinement
Use the ArrayRefinement to call sort_naturally_by directly on arrays:
using Philiprehberger::NaturalSort::ArrayRefinement
items = [
OpenStruct.new(name: "img10"),
OpenStruct.new(name: "img2"),
OpenStruct.new(name: "img1")
]
items.sort_naturally_by { |x| x.name }
# => [#<OpenStruct name="img1">, #<OpenStruct name="img2">, #<OpenStruct name="img10">]
Comparing Two Strings
Philiprehberger::NaturalSort.compare("file2", "file10")
# => -1
Philiprehberger::NaturalSort.compare("file10", "file2")
# => 1
Using a Comparator Proc
cmp = Philiprehberger::NaturalSort.comparator
["file10", "file2", "file1"].sort(&cmp)
# => ["file1", "file2", "file10"]
Reverse Sorting
Philiprehberger::NaturalSort.sort(["file10", "file2", "file1"], reverse: true)
# => ["file10", "file2", "file1"]
items = [{ name: "img10" }, { name: "img2" }, { name: "img1" }]
sorted = Philiprehberger::NaturalSort.sort_by(items, reverse: true) { |x| x[:name] }
# => [{ name: "img10" }, { name: "img2" }, { name: "img1" }]
Stable Sorting
Preserves original order for elements that compare as equal:
Philiprehberger::NaturalSort.sort_stable(["file1", "FILE1", "file2"])
# => ["file1", "FILE1", "file2"]
Min and Max
Find the naturally smallest or largest element without a full sort:
Philiprehberger::NaturalSort.min(["file10", "file2", "file1"])
# => "file1"
Philiprehberger::NaturalSort.max(["file10", "file2", "file1"])
# => "file10"
Sort Key for Built-in Methods
Use natural_key with Ruby's standard sort_by, min_by, max_by, group_by, and other Enumerable methods:
files = ["file10.txt", "file2.txt", "file1.txt"]
files.sort_by { |f| Philiprehberger::NaturalSort.natural_key(f) }
# => ["file1.txt", "file2.txt", "file10.txt"]
files.min_by { |f| Philiprehberger::NaturalSort.natural_key(f) }
# => "file1.txt"
Stable Sort with Block
Preserves original order for elements whose block values compare as equal:
items = [
{ name: "file1", id: "a" },
{ name: "FILE1", id: "b" },
{ name: "file2", id: "c" }
]
sorted = Philiprehberger::NaturalSort.sort_by_stable(items) { |x| x[:name] }
# => [{ name: "file1", id: "a" }, { name: "FILE1", id: "b" }, { name: "file2", id: "c" }]
Case-Sensitive Mode
Philiprehberger::NaturalSort.sort(["Banana", "apple"], case_sensitive: true)
# => ["Banana", "apple"]
Case-Insensitive Sorting
Use ignore_case: true to force case-insensitive comparison, overriding case_sensitive:
Philiprehberger::NaturalSort.sort(["Banana", "apple", "Cherry"], ignore_case: true)
# => ["apple", "Banana", "Cherry"]
Philiprehberger::NaturalSort.sort(["Banana", "apple"], case_sensitive: true, ignore_case: true)
# => ["apple", "Banana"]
Group by Prefix
Split strings at the first digit boundary and group by the non-numeric prefix. Each group's values are naturally sorted:
Philiprehberger::NaturalSort.group_by_prefix(["file1", "file2", "file10", "img3", "img20"])
# => { "file" => ["file1", "file2", "file10"], "img" => ["img3", "img20"] }
Collate Comparator
Spaceship-style comparator returning -1, 0, or 1. Suitable for use with Array#sort:
["file10", "file2", "file1"].sort { |a, b| Philiprehberger::NaturalSort.collate(a, b) }
# => ["file1", "file2", "file10"]
API
| Method | Description |
|---|---|
NaturalSort.sort(array, case_sensitive: false, reverse: false, ignore_case: false) |
Sort an array of strings in natural order |
| `NaturalSort.sort_by(array, case_sensitive: false, reverse: false, ignore_case: false) { \ | x\ |
NaturalSort.sort_stable(array, case_sensitive: false) |
Stable sort preserving original order for equal elements |
NaturalSort.min(array, case_sensitive: false) |
Find the naturally smallest element |
NaturalSort.max(array, case_sensitive: false) |
Find the naturally largest element |
NaturalSort.compare(a, b, case_sensitive: false) |
Compare two strings, returns -1, 0, or 1 |
NaturalSort.collate(a, b, case_sensitive: false) |
Spaceship-style comparator returning -1, 0, or 1 |
NaturalSort.comparator(case_sensitive: false) |
Returns a reusable comparison Proc |
NaturalSort.natural_key(str, case_sensitive: false) |
Returns a sort key for use with sort_by, min_by, etc. |
| `NaturalSort.sort_by_stable(array, case_sensitive: false) { \ | x\ |
NaturalSort.group_by_prefix(array, case_sensitive: false) |
Group strings by non-numeric prefix with naturally sorted values |
| `array.sort_naturally_by { \ | x\ |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: