Wie vergleiche ich Versionen in Ruby?


119

Wie schreibe ich einen Code, um einige Versionszeichenfolgen zu vergleichen und die neuesten zu erhalten?

Zum Beispiel Zeichenfolgen wie : '0.1', '0.2.1', '0.44'.


Ich musste vor einiger Zeit pessimistische Versionsbeschränkungen vergleichen, wollte mich aber nicht auf RubyGems verlassen, also schrieb ich eine einfache VersionKlasse, die alles tut, was ich brauche: shorts.jeffkreeftmeijer.com/2014/…
jkreeftmeijer

Antworten:


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

14
Die Gem::Version...Syntax ließ mich denken, ich müsste ein Juwel installieren. Aber es war nicht erforderlich.
Guillaume

Hinweis: Dies gibt für mich unter Ruby 1.x einen Fehler bezüglich der undefinierten Variablen 'Gem' aus, funktioniert jedoch unter Ruby 2.x wie erwartet. In meinem Fall habe ich RUBY_VERSION gegen Ruby 1.x (nicht 2.x) geprüft, also habe ich einfach RUBY_VERSION.split ('.') [0] == "1" gemacht, wie es John Hyland und DigitalRoss tun.
uliwitness

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

6
@uliwitness es ist nicht Ruby 1.x vs 2.x; es ist 1.8.x vs 1.9+. Ruby bis 1.8.x enthält standardmäßig keine Rubygems. Sie benötigen eine require 'rubygems', um Zugriff auf den GemNamespace zu erhalten. Ab 1.9 ist es jedoch automatisch enthalten.
Mark Reed

Dies funktionierte auch für den Vergleich von Wildcard-NPM-Versionen. +1
deepelement


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
Wie bei einigen anderen Antworten hier sieht es so aus, als würden Sie Zeichenfolgenvergleiche anstelle von numerischen durchführen, was beim Vergleich von Versionen wie '0.10' und '0.4' zu Problemen führen wird.
John Hyland

7
Upvoted für eine prägnante Lösung, bei der kein Edelstein installiert werden muss.
JD.

2
Für das, was es wert ist: 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 macht es hässlich, aber im Grunde ist die Verwendung dieser vs Gem :: Version etwa doppelt so schnell.
Shai

Eine Version ist jedoch kein Array.
Sergio Tulentsev

15

Sie können den VersionomyEdelstein verwenden (erhältlich bei 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
Ich habe das gesehen, aber ich muss 2 Edelsteine ​​verwenden, um eine wirklich einfache Sache zu machen. Ich möchte das als letzte Wahl verwenden.
user239895

8
"Das Rad nicht neu erfinden". Weil es einfach ist, heißt das nicht, dass der Programmierer keine Arbeit und Gedanken hineingesteckt hat. Verwenden Sie den Edelstein, lesen Sie den Code und lernen Sie daraus - und gehen Sie zu größeren und besseren Dingen über!
Trevoke

Das Abhängigkeitsmanagement und die Versionspflege sind ein schwieriges Problem, wahrscheinlich viel schwieriger als die Aufgabe, zwei Versionen zu vergleichen. Ich stimme voll und ganz zu, dass die Einführung von zwei weiteren Abhängigkeiten in diesem Fall das letzte Mittel sein sollte.
Kkodev

10

Ich würde es tun

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

Dann können Sie tun

a1 <=> a2

(und wahrscheinlich alle anderen "üblichen" Vergleiche).

... und wenn Sie ein <oder >testen möchten , können Sie z

(a1 <=> a2) < 0

oder machen Sie noch etwas Funktionsumbruch, wenn Sie dazu neigen.


1
Array.class_eval {include Comparable} lässt alle Arrays auf <,> usw. reagieren. Oder wenn Sie dies nur für bestimmte Arrays tun möchten: a = [1, 2]; a.extend (vergleichbar)
Wayne Conrad

4
Das Problem, das ich bei dieser Lösung gefunden habe, ist, dass "1.2.0" größer als "1.2" ist
Maria S

9

Gem::Version ist der einfache Weg hierher zu gehen:

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

Viel besser als eine Versionomie, die eine C-Erweiterung erfordert!?
W. Andrew Loe III

Ich glaube nicht, dass 'max' funktionieren wird. Es wird 0,5 als größer als 0,44 melden. Was beim Vergleich von Semver-Versionen nicht stimmt.
Flo Woo

2
Dies scheint in der neuesten Gem :: Version behoben worden zu sein. 0,44 wird jetzt korrekt als höher als 0,5 gemeldet.
Flo Woo

5

Wenn Sie es von Hand machen möchten, ohne Edelsteine ​​zu verwenden, sollte etwas wie das Folgende funktionieren, obwohl es ein wenig perly aussieht.

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

Im Wesentlichen wandeln Sie jede Versionszeichenfolge in ein Array von Ganzzahlen um und verwenden dann den Array-Vergleichsoperator . Sie können die Komponentenschritte aufteilen, um etwas leichter zu befolgen, wenn dies in Code enthalten ist, den jemand warten muss.


-1

Ich hatte das gleiche Problem, ich wollte einen Versionskomparator ohne Edelsteine, und fand Folgendes:

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.