¿Cómo comparar versiones en Ruby?


119

¿Cómo escribir un fragmento de código para comparar algunas cadenas de versiones y obtener la más nueva?

Por ejemplo cadenas como: '0.1', '0.2.1', '0.44'.


Necesitaba comparar las restricciones de la versión pesimista hace un tiempo, pero no quería depender de RubyGems para hacerlo, así que escribí una Versionclase simple que hace todo lo que necesito: shorts.jeffkreeftmeijer.com/2014/…
jkreeftmeijer

Respuestas:


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

14
La Gem::Version...sintaxis me hizo pensar que necesitaría instalar una gema. Pero no fue necesario.
Guillaume

Nota: Esto me da un error sobre la variable indefinida 'Gem' en Ruby 1.x, pero funciona como se esperaba en Ruby 2.x. En mi caso, estaba comprobando que RUBY_VERSION no fuera Ruby 1.x (no 2.x), así que hice RUBY_VERSION.split ('.') [0] == "1" como lo hacen John Hyland y DigitalRoss.
uliwitness

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

6
@uliwitness no es Ruby 1.x vs 2.x; es 1.8.x frente a 1.9+. Ruby hasta 1.8.x no incluye rubygems por defecto; necesita un require 'rubygems'para obtener acceso al Gemespacio de nombres. Sin embargo, a partir de la versión 1.9, se incluye automáticamente.
Mark Reed

Esto también funcionó para comparar versiones comodín de NPM. +1
profundización


19
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')]

3
Al igual que algunas de las otras respuestas aquí, parece que está haciendo comparaciones de cadenas en lugar de numéricas, lo que causará problemas al comparar versiones como '0.10' y '0.4'.
John Hyland

7
Votado a favor por una solución concisa que no requiere la instalación de una gema.
JD.

2
Por lo que vale: vers = (1..3000000).map{|x| "0.0.#{x}"}; 'ok' puts Time.now; vers.map{|v| ComparableVersion.new(v) }.sort.first; puts Time.now # 24 seconds 2013-10-29 13:36:09 -0700 2013-10-29 13:36:33 -0700 => nil puts Time.now; vers.map{|v| Gem::Version.new(v) }.sort.first; puts Time.now # 41 seconds 2013-10-29 13:36:53 -0700 2013-10-29 13:37:34 -0700 Code blob lo está haciendo feo, pero básicamente, usar esto vs Gem :: Version es aproximadamente el doble de rápido.
Shai

Sin embargo, una versión no es una matriz.
Sergio Tulentsev

15

Puedes usar la Versionomygema (disponible en 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

4
Lo he visto, pero requiere que use 2 gemas para hacer algo realmente simple. Quiero usar eso como última opción.
user239895

8
"No reinventes la rueda". El hecho de que sea simple no significa que el programador no haya trabajado ni pensado en ello. Usa la gema, lee el código y aprende de él, ¡y pasa a cosas más grandes y mejores!
Trevoke

La gestión de dependencias y el mantenimiento de versiones es un problema difícil, probablemente mucho más difícil que la tarea de comparar 2 versiones. Estoy totalmente de acuerdo en que introducir 2 dependencias más debería ser un último recurso en este caso.
kkodev

10

yo lo haría

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

Entonces puedes hacer

a1 <=> a2

(y probablemente todas las demás comparaciones "habituales").

... y si quieres una prueba <o >, puedes hacer, por ejemplo,

(a1 <=> a2) < 0

o hacer un envoltorio más funcional si así lo desea.


1
Array.class_eval {include Comparable} hará que todas las matrices respondan a <,>, etc. O, si solo desea hacer esto con ciertas matrices: a = [1, 2]; a.extend (Comparable)
Wayne Conrad

4
El problema que encontré con esta solución es que devuelve que "1.2.0" es más grande que "1.2"
Maria S

9

Gem::Version es la forma más fácil de ir aquí:

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

¿¡Mucho mejor que versionomy que requiere una extensión c !?
W. Andrew Loe III

No creo que 'max' funcione ... informará que 0,5 es mayor que 0,44. Lo cual no es cierto al comparar versiones de semver.
Flo Woo

2
esto parece haberse solucionado en la última versión de Gem ::. 0,44 se informa correctamente como superior a 0,5 ahora.
Flo Woo

5

Si desea hacerlo a mano sin usar gemas, algo como lo siguiente debería funcionar, aunque tiene un aspecto un poco perverso.

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

Básicamente, convierte cada cadena de versión en una matriz de enteros y luego usa el operador de comparación de matriz . Puede dividir los pasos de los componentes para obtener algo un poco más fácil de seguir si esto va en el código que alguien necesitará mantener.


-1

Tuve el mismo problema, quería un comparador de versiones sin gemas, se me ocurrió esto:

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.