user239895
user239895

Reputation: 1681

How to compare versions in Ruby?

How to write a piece of code to compare some versions strings and get the newest?

For example strings like: '0.1', '0.2.1', '0.44'.

Upvotes: 136

Views: 31704

Answers (8)

Wivlaro
Wivlaro

Reputation: 1515

I had the same problem, I wanted a Gem-less version comparator, came up with this:

def compare_versions(versionString1,versionString2)
    v1 = versionString1.split('.').collect(&:to_i)
    v2 = versionString2.split('.').collect(&:to_i)
    #pad with zeroes so they're the same length
    while v1.length < v2.length
        v1.push(0)
    end
    while v2.length < v1.length
        v2.push(0)
    end
    for pair in v1.zip(v2)
        diff = pair[0] - pair[1]
        return diff if diff != 0
    end
    return 0
end

Upvotes: -1

levinalex
levinalex

Reputation: 5949

If you need to check pessimistic version constraints, you can use Gem::Dependency like this:

Gem::Dependency.new('', '~> 1.4.5').match?('', '1.4.6beta4')

Upvotes: 43

grosser
grosser

Reputation: 15097

Gem::Version.new('0.4.1') > Gem::Version.new('0.10.1')

Upvotes: 270

Mark Reed
Mark Reed

Reputation: 95242

Gem::Version is the easy way to go here:

%w<0.1 0.2.1 0.44>.map {|v| Gem::Version.new v}.max.to_s
=> "0.44"

Upvotes: 9

DigitalRoss
DigitalRoss

Reputation: 146053

class Version < Array
  def initialize s
    super(s.split('.').map { |e| e.to_i })
  end
  def < x
    (self <=> x) < 0
  end
  def > x
    (self <=> x) > 0
  end
  def == x
    (self <=> x) == 0
  end
end
p [Version.new('1.2') < Version.new('1.2.1')]
p [Version.new('1.2') < Version.new('1.10.1')]

Upvotes: 22

John Hyland
John Hyland

Reputation: 6872

If you want to do it by hand without using any gems, something like the following should work, though it's a little perly looking.

versions = [ '0.10', '0.2.1', '0.4' ]
versions.map{ |v| (v.split '.').collect(&:to_i) }.max.join '.'

Essentially, you turn each version string in to an array of integers and then use the array comparison operator. You could break out the component steps to get something a little easier to follow if this is going in code somebody will need to maintain.

Upvotes: 5

Carl Smotricz
Carl Smotricz

Reputation: 67750

I would do

a1 = v1.split('.').map{|s|s.to_i}
a2 = v2.split('.').map{|s|s.to_i}

Then you can do

a1 <=> a2

(and probably all the other "usual" comparisons).

...and if you want a < or > test, you can do e.g.

(a1 <=> a2) < 0

or do some more function wrapping if you're so inclined.

Upvotes: 11

notnoop
notnoop

Reputation: 59299

You can use the Versionomy gem (available at github):

require 'versionomy'

v1 = Versionomy.parse('0.1')
v2 = Versionomy.parse('0.2.1')
v3 = Versionomy.parse('0.44')

v1 < v2  # => true
v2 < v3  # => true

v1 > v2  # => false
v2 > v3  # => false

Upvotes: 15

Related Questions