Is there a natural_sort_by method for Ruby?
As long as files are always named "file #"
, you could do
files.sort_by{|f| f.name.split(" ")[1].to_i }
This splits on the space, and grabs the number to do the sorting.
natural sort an array of hashes in Ruby
First of all, your my_array
is JavaScript/JSON so I'll assume that you really have this:
my_array = [
{"id" => "some-server-1", "foo" => "bar"},
{"id" => "some-server-2", "foo" => "bat"},
{"id" => "some-server-10", "foo" => "baz"}
]
Then you just need to sort_by
the numeric suffix of the 'id'
values:
my_array.sort_by { |e| e['id'].sub(/^some-server-/, '').to_i }
If the "some-server-" prefixes aren't always "some-server-" then you could try something like this:
my_array.sort_by { |e| e['id'].scan(/\D+|\d+/).map { |x| x =~ /\d/ ? x.to_i : x } }
That would split the 'id'
values into numeric and non-numeric pieces, convert the numeric pieces to integers, and then compare the mixed string/integers arrays using the Array <=>
operator (which compares component-wise); this will work as long as the numeric and non-numeric components always match up. This approach would handle this:
my_array = [
{"id" => "some-server-1", "foo" => "bar"},
{"id" => "xxx-10", "foo" => "baz"}
]
but not this:
my_array = [
{"id" => "11-pancakes-23", "foo" => "baz"},
{"id" => "some-server-1", "foo" => "bar"}
]
If you need to handle this last case then you'd need to compare the arrays entry-by-entry by hand and adjust the comparison based on what you have. You could still get some of the advantages of the sort_by
Schwartzian Transform with something like this (not very well tested code):
class NaturalCmp
include Comparable
attr_accessor :chunks
def initialize(s)
@chunks = s.scan(/\D+|\d+/).map { |x| x =~ /\d/ ? x.to_i : x }
end
def <=>(other)
i = 0
@chunks.inject(0) do |cmp, e|
oe = other.chunks[i]
i += 1
if(cmp == 0)
cmp = e.class == oe.class \
? e <=> oe \
: e.to_s <=> oe.to_s
end
cmp
end
end
end
my_array.sort_by { |e| NaturalCmp.new(e['id']) }
The basic idea here is to push the comparison noise off to another class to keep the sort_by
from degenerating into an incomprehensible mess. Then we use the same scanning as before to break the strings into pieces and implement the array <=>
comparator by hand. If we have two things of the same class then we let that class's <=>
deal with it otherwise we force both components to String and compare them as such. And we only care about the first non-0 result.
How to sort file names in ruby?
Eric's answer is great way of doing a Natural Sort Order for the digits only in file names. Works if all file names have the same prefix.
If you want to add a second element (for example, file names that do not have digits in them) you can great a multi-element sort_by be creating a list:
filenames = ["file1.txt", "file11.txt", "file12.txt", "file2.txt", "file3.txt","file.txt", "File.txt"]
filenames.sort_by{ |name| [name[/\d+/].to_i, name] }
=> ["File.txt", "file.txt", "file1.txt", "file2.txt", "file3.txt", "file11.txt", "file12.txt"]
The two element of the sort_by
implements:
- Integer value of digits if any are found with a regex
name[/\d+/].to_i
then - Name if no digits or same digits
name
.
More robustly, you can split the entire string by digits and convert each to an int:
> "abc123def456gh".split(/(\d+)/).map{ |e| Integer(e) rescue e}
=> ["abc", 123, "def", 456, "gh"]
So your Natural Sort becomes:
arr.sort_by{ |s| s.split(/(\d+)/).map{ |e| Integer(e) rescue e}}
So now names and numbers (even multiples names and numbers) are handled correctly:
> arr = ["file1.txt", "file11.txt", "file12.txt", "file2.txt", "file3.txt", "gfile10.txt", "gfile1.txt", "gfile.txt", "file.txt", "afile.txt","afile10.txt","afile2.txt" ]
> arr.sort_by{ |s| s.split(/(\d+)/).map{ |e| Integer(e) rescue e}}
=> ["afile2.txt", "afile10.txt", "afile.txt", "file1.txt", "file2.txt", "file3.txt", "file11.txt", "file12.txt", "file.txt", "gfile1.txt", "gfile10.txt", "gfile.txt"]
Ruby how to override ruby sort_by
class Array
def natural_sort_by
sort_by{|x| MyModule.naturalize_str(yield(x))}
end
end
or
class Array
def natural_sort_by &block
sort_by{|x| MyModule.naturalize_str(block.call(x))}
end
end
Is there a method to limit/clamp a number?
Ruby 2.4.0 introduces Comparable#clamp
:
523.clamp(0, 100) #=> 100
How to sort chapter numbers like 1.1.1, 1.2.1, 1.2.46, etc.?
In Ruby, Array
s are ordered lexicographically, so the easiest way would be to convert these into Array
s and then sort them:
chapters = %w[1.1.1 1.1.2 1.2.1 1.3.1 1.3.2 1.3.3 10.42.64]
chapters.sort_by {|chapter| chapter.split('.').map(&:to_i) }
# => ["1.1.1", "1.1.2", "1.2.1", "1.3.1", "1.3.2", "1.3.3", "10.42.64"]
Of course, the real solution would be to use objects instead of slugging around arrays of strings of numbers. After all, Ruby is an object-oriented language, not an arrays-of-strings-of-numbers-oriented language:
class ChapterNumber
include Comparable
def initialize(*nums)
self.nums = nums
end
def <=>(other)
nums <=> other.nums
end
def to_s
nums.join('.')
end
alias_method :inspect, :to_s
protected
attr_reader :nums
private
attr_writer :nums
end
chapters = [ChapterNumber.new(1, 1, 1), ChapterNumber.new(1, 1, 2),
ChapterNumber.new(1, 2, 1), ChapterNumber.new(1, 3, 1),
ChapterNumber.new(1, 3, 2), ChapterNumber.new(1, 3, 3),
ChapterNumber.new(10, 42, 64)]
chapters.sort
# => [1.1.1, 1.1.2, 1.2.1, 1.3.1, 1.3.2, 1.3.3, 10.42.64]
ruby aasm gem: event name change, but permitted method still ask for old event name
You will need to restart your rails console/server once you have made changes to the state machine events.
Related Topics
How to Encode Media in Base64 Given Url in Ruby
Iterate Over a Deeply Nested Level of Hashes in Ruby
Rails 3.1 Pipeline - Exclude JavaScript File
Rspec/Capybara Loading in Progress, Circular Require Considered Harmful
Has_Many :Through with Counter_Cache
Block Syntax Difference Causes "Localjumperror: No Block Given (Yield)"
How to Get Sinatra to Auto-Reload the File After Each Change
How to Get Haml to Work with Rails
Using Join Tables in Ruby on Rails
Ruby: How to Add "# Encoding: Utf-8" Automatically
Ruby: Dynamically Generate Attribute_Accessor
Differencebetween Link_To, Redirect_To, and Render