Come confrontare le versioni in Ruby?


119

Come scrivere un pezzo di codice per confrontare alcune stringhe di versioni e ottenere la più recente?

Per esempio stringhe come: '0.1', '0.2.1', '0.44'.


Qualche tempo fa avevo bisogno di confrontare i vincoli della versione pessimistica, ma non volevo dipendere da RubyGems per farlo, quindi ho scritto una semplice Versionclasse che fa tutto ciò di cui avevo bisogno: shorts.jeffkreeftmeijer.com/2014/…
jkreeftmeijer

Risposte:


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

14
La Gem::Version...sintassi mi ha fatto pensare che avrei bisogno di installare una gemma. Ma non era richiesto.
Guillaume

Nota: questo dà un errore sulla variabile non definita "Gem" per me su Ruby 1.x, ma funziona come previsto su Ruby 2.x. Nel mio caso stavo controllando RUBY_VERSION contro Ruby 1.x (non 2.x), quindi ho appena fatto RUBY_VERSION.split ('.') [0] == "1" come fanno John Hyland e DigitalRoss.
Uli testimone

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

6
@uliwitness non è Ruby 1.x vs 2.x; è 1.8.x contro 1.9+. Ruby fino alla 1.8.x non include rubygem per impostazione predefinita; è necessario un require 'rubygems'per accedere allo Gemspazio dei nomi. Dalla 1.9 in poi, tuttavia, è automaticamente incluso.
Mark Reed

Questo ha funzionato anche per confrontare le versioni NPM con caratteri jolly. +1
elemento profondo


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
Come alcune delle altre risposte qui, sembra che tu stia facendo confronti di stringhe anziché numerici, il che causerà problemi quando si confrontano versioni come "0.10" e "0.4".
John Hyland

7
Votato per una soluzione concisa che non richiede l'installazione di una gemma.
JD.

2
Per quello che vale: il 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 blob di codice lo rende brutto, ma fondamentalmente, usare questa versione rispetto a Gem :: è circa il doppio più veloce.
Shai

Tuttavia, una versione non è un array.
Sergio Tulentsev

15

Puoi usare la Versionomygemma (disponibile su 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
L'ho visto, ma richiedimi di usare 2 gemme per fare una cosa davvero semplice. Voglio usarlo come ultima scelta.
user239895

8
"Non reinventare la ruota". Perché è semplice non significa che il programmatore non ci abbia lavorato e pensato. Usa la gemma, leggi il codice e impara da esso e passa a cose più grandi e migliori!
Trevoke

La gestione delle dipendenze e la manutenzione delle versioni è un problema difficile, probabilmente molto più difficile del compito di confrontare 2 versioni. Sono totalmente d'accordo che l'introduzione di altre 2 dipendenze dovrebbe essere l'ultima risorsa in questo caso.
kkodev

10

farei

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

Allora puoi farlo

a1 <=> a2

(e probabilmente tutti gli altri "soliti" confronti).

... e se vuoi un test <o >, puoi fare ad es

(a1 <=> a2) < 0

o fai un po 'più di wrapping delle funzioni se sei così incline.


1
Array.class_eval {include Comparable} farà sì che tutti gli array rispondano a <,>, ecc. Oppure, se vuoi farlo solo a determinati array: a = [1, 2]; a. estendere (confrontabile)
Wayne Conrad

4
Il problema che ho riscontrato con questa soluzione è che restituisce che "1.2.0" è maggiore di "1.2"
Maria S

9

Gem::Version è il modo più semplice per andare qui:

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

Molto meglio di versionomy che richiede un'estensione c !?
W. Andrew Loe III

non penso che 'max' funzionerà .. indicherà che 0,5 è maggiore di 0,44. Il che non è vero quando si confrontano le versioni semver.
Flo Woo

2
questo sembra essere stato risolto nell'ultima versione di Gem ::. 0.44 è riportato correttamente come maggiore di 0,5 ora.
Flo Woo

5

Se vuoi farlo a mano senza usare nessuna gemma, qualcosa come il seguente dovrebbe funzionare, anche se sembra un po 'perly.

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

In sostanza, si trasforma ogni stringa di versione in una matrice di numeri interi e quindi si utilizza l' operatore di confronto di matrice . Potresti suddividere i passaggi dei componenti per ottenere qualcosa di un po 'più facile da seguire se questo sta andando in codice che qualcuno dovrà mantenere.


-1

Ho avuto lo stesso problema, volevo un comparatore di versioni senza gemme, mi è venuto in mente questo:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.