A natural sort for Ruby.
Unnatural defines a natural sort as one where:
- comparison is case-insensitive
- consecutive sequences of digits are compared according to their numeric value (not their ascii values)
Unnatural does not (currently) provide support for:
- non-ASCII-compatible encoding (although the pure ruby comparison functions seem to work)
- any number representation other than simple decimal integers
- whitespace insensitivity (i.e., one space and two spaces can be considered as different)
Unnatural provides four algorithms, all of which use Ruby's built-in quicksort as the fundamental sort algorithm. All four modules provide module methods .sort
for simply sorting an enumerable, .sort_by
for a memoized sort according to a block, and .compare
for spaceship-operator-style comparison.
Compares strings byte-by-byte. Comparison function implemented in C. Does not appear to sort unicode strings correctly. Much faster than any of the pure Ruby options. The default.
Compares strings using a StringScanner
. Pure ruby. Tends to be outperformed by Unnatural::Substitution
and Unnatural::Split
when sorting short strings via the global sort function, but its comparison function is the fastest of the pure-ruby algorithms.
Compares strings by spliting them into arrays of alternating string and integer values. Pure Ruby. Tends to be outperformed by the others.
Compares strings by zero-padding integer sequences such that all are the same length. Pure Ruby. Tends to be outperformed by Unnatural::Scan
on longer strings. Recommended for sorting short unicode strings.
Add this line to your application's Gemfile:
gem 'unnatural'
And then execute:
$ bundle
Or install it yourself as:
$ gem install unnatural
Sorting an enumerable of strings:
require 'unnatural'
sorted = Unnatural.sort(some_array_of_strings)
Sorting an enumerable of objects according to a block:
sorted = Unnatural.sort_by(some_array_of_objects) { |e| e.name }
sorted = Unnatural.sort_by(some_array_of_objects, &:name)
Defining the comparison method for a class explicitly:
require 'unnatural'
class User
def <=>(other)
Unnatural.compare(name, other.name)
end
end
Or by defining #to_str
and using Unnatural as a mix-in:
# this is equivalent to the last example
class User
include Unnatural
def to_str
name
end
end
The default can be changed throughout an application:
# use Scan instead of Fast
Unnatural.algorithm = Unnatural::Scan
Or you can use the .compare
and .sort
functions for an algorithm's module directly:
sorted_short_strings = Unnatural::Substitution.sort(some_short_strings)
sorted_long_strings = Unnatural::Scan.sort(some_long_strings)
class User
def <=>(other)
Unnatural::Scan.compare(name, other.name)
end
end
There are two other natural sort gems:
https://github.com/dogweather/naturally
https://github.com/johnnyshields/naturalsort
Unnatural took test cases from each one.
After checking out the repo, run bin/setup
to install dependencies. Then, run rake test
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/bjmllr/unnatural. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
The gem is available as open source under the terms of the MIT License.