How to Compare Versions in Ruby

How does Ruby compare semantic version strings?

It checks the ordinal of each individual character in the string. It stops the first time there is a mismatch on the same index. The higher the ordinal, the "bigger" the character is. Basically, it something like:

first_string.chars.map(&:ord) >= second_string.chars.map(&:ord)

As pointed in the comments, this doesn't lead to natural ordering, hence why people use Gem::Version:

'11' > '9' # => false

Determine order of version numbers in ruby

The natural first step is to split the version "numbers" into numeric components:

v1 = '2.1.10'.split('.').map(&:to_i) # [ 2, 1, 10 ]
v2 = '2.1.3'.split('.').map(&:to_i) # [ 2, 1, 3 ]

Then note that Array#<=> compares arrays element by element so you can use it to whip up a quick'n'dirty < implementation for arrays. You can use <=> to monkey patch #< and #> methods into Array or just do it inline:

if (v1 <=> v2) < 0
# v1 < v2
elsif (v1 <=> v2) > 0
# v1 > v2
else
# v1 == v2
end

<=> is specified to return -1, 0, or +1 so you could use

if (v1 <=> v2) == -1
# v1 < v2
elsif (v1 <=> v2) == 1
# v1 > v2
else
# v1 == v2
end

too; I prefer the first version as the < 0 and > 0 are more suggestive as to the intent.

You'd probably want to hide all that behind a method somewhere or maybe even monkey patch String to have a version comparing method.

The above also means that 2.1 is a lower version number than, say, 2.1.11 so you don't have to worry about having the same number of components.

I am assuming that you're working with versions whose components are decimal numbers.

What is the best approach to compare two files dates in Ruby?

mtime gives you a Time, not a string.
You can "subtract" Time objects, resulting in seconds. If a and b are both files, you can do something like

(b.mtime - a.mtime).between?(0, 120)

which would return false if b is more then 2 minutes older than a.

Version sort (with alphas, betas, etc.) in ruby

Ruby ships with the Gem class, which knows about versions:

ar = ['10.0.0b12', '10.0.0b3', '10.0.0a2', '9.0.10', '9.0.3']

p ar.sort_by { |v| Gem::Version.new(v) }
# => ["9.0.3", "9.0.10", "10.0.0a2", "10.0.0b3", "10.0.0b12"]

How to check if the RUBY_VERSION is greater than a certain version?

Ruby's Gem library can do version number comparisons:

require 'rubygems' # not needed with Ruby 1.9+

ver1 = Gem::Version.new('1.8.7') # => #<Gem::Version "1.8.7">
ver2 = Gem::Version.new('1.9.2') # => #<Gem::Version "1.9.2">
ver1 <=> ver2 # => -1

See http://rubydoc.info/stdlib/rubygems/1.9.2/Gem/Version for more info.

How to find a bigger version number?

An idea: create a Object#compare_by method that behaves like compare (aka the spaceship operator Object#<=>) but takes a custom block:

class Object
def compare_by(other)
yield(self) <=> yield(other)
end
end

>> "1.5.2".compare_by("1.5.7") { |s| s.split(".").map(&:to_i) }
#=> -1

You can also take a more specific approach still based on the compare method:

class String
def compare_by_fields(other, fieldsep = ".")
cmp = proc { |s| s.split(fieldsep).map(&:to_i) }
cmp.call(self) <=> cmp.call(other)
end
end

>> "1.5.8".compare_by_fields("1.5.8")
#=> 0


Related Topics



Leave a reply



Submit