philiprehberger-natural_sort

Tests Gem Version Last updated

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:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT