2010-01-12 17 views
97

Wie schreibe ich ein Stück Code, um einige Versionen Strings zu vergleichen und das neueste zu bekommen?Wie vergleicht man Versionen in Ruby?

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

+0

Gute Frage! – Robert

+0

Ich musste vor einiger Zeit pessimistische Versionsbeschränkungen vergleichen, aber ich wollte nicht auf RubyGems angewiesen sein, um dies zu tun, also schrieb ich eine einfache 'Version'-Klasse, die alles was ich brauche: http://shorts.jeffkreeftmeijer.com/2014/compare-version-numbers-with-pessimistische-constraints/ – jkreeftmeijer

Antwort

15

Sie können das Versionomy gem (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 
+3

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

"Erfinden Sie das Rad nicht neu". Weil es einfach ist, bedeutet das nicht, dass der Programmierer keine Arbeit und Gedanken in das Programm gesteckt hat. Nutze das Juwel, lies den Code und lerne daraus - und mach weiter mit größeren und besseren Dingen! – Trevoke

+0

Dependency Management und Versionsverwaltung ist ein schwieriges Problem, wahrscheinlich viel schwieriger als die Aufgabe, 2 Versionen zu vergleichen. Ich stimme völlig zu, dass die Einführung von zwei weiteren Abhängigkeiten in diesem Fall der letzte Ausweg sein sollte. – kkodev

9

I

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

tun würde, dann können Sie tun

a1 <=> a2 

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

... und wenn Sie einen < oder > Test möchten, können Sie z.B.

(a1 <=> a2) < 0 

oder machen Sie weitere Funktionen, wenn Sie so geneigt sind.

+1

Array.class_eval {include Comparable} lässt alle Arrays auf <, > usw. reagieren. Oder wenn Sie das nur mit bestimmten Arrays machen wollen: a = [1, 2]; a.extend (Vergleichbar) –

+0

Cool, danke!(15 Zeichen) –

+3

Das Problem, das ich mit dieser Lösung fand, ist, dass es zurückgibt, dass "1.2.0" größer ist als "1.2" –

3

Wenn Sie es von Hand tun möchten, ohne irgendwelche Edelsteine ​​zu verwenden, sollte etwas wie das Folgende funktionieren, obwohl es ein bisschen perlig aussieht.

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

Im Wesentlichen Sie jede Version String drehen in einem Array von ganzen Zahlen und dann die array comparison operator verwenden. Sie könnten die Komponentenschritte durchbrechen, um etwas leichter zu folgen, wenn dies in Code geht, den jemand pflegen muss.

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 einige der anderen Antworten hier, sieht es so aus, als würden Sie String-Vergleiche statt Dies wird Probleme beim Vergleich von Versionen wie '0.10' und '0.4' verursachen. –

+1

Guter Punkt; jetzt behoben. – DigitalRoss

+7

Upvoted für präzise Lösung, die keine Installation eines Edelsteins erfordert. –

189
Gem::Version.new('0.4.1') > Gem::Version.new('0.10.1') 
+11

Die 'Gem :: Version ...' Syntax ließ mich denken, dass ich einen Edelstein installieren müsste. Aber es war nicht erforderlich. – Guillaume

+0

Hinweis: Dies gibt einen Fehler über undefinierte Variable 'Gem' für mich auf Ruby 1.x, aber funktioniert wie erwartet auf Ruby 2.x. In meinem Fall habe ich RUBY_VERSION gegen Ruby 1.x (nicht 2.x) geprüft, also habe ich RUBY_VERSION.split ('.') [0] == "1" wie John Hyland und DigitalRoss gemacht. – uliwitness

+5

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

7

Gem::Version ist der einfache Weg, hier gehen:

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

Viel besser als Versionomy, die eine c-Erweiterung benötigt !? –

+0

Ich glaube nicht, dass 'max' funktioniert .. es wird 0,5 als größer als 0,44 gemeldet. Was beim Vergleich von sverver-Versionen nicht zutrifft. –

+2

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

30

Wenn Sie pessimistic version constraints überprüfen müssen, Sie Gem::Dependency wie diese verwenden:

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

Neuere Versionen scheinen eine Zeichenfolge für den Namen zu benötigen. Ein leerer String funktioniert einwandfrei, d. H. "Gem :: Dependency.new ('', '~> 1.4.5'). Match? ('', '1.4.6beta4')' –

+0

danke. aktualisierte die Antwort. – levinalex

-1

ich hatte Das gleiche Problem, ich wollte eine Gem-less Version Komparator, kam mit dieser:

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 
Verwandte Themen