Bagaimana cara membandingkan versi di Ruby?

119

Bagaimana cara menulis sepotong kode untuk membandingkan beberapa string versi dan mendapatkan yang terbaru?

Sebagai contoh string seperti: '0.1', '0.2.1', '0.44'.

pengguna239895
sumber
Saya perlu membandingkan batasan versi pesimis beberapa waktu lalu, tetapi saya tidak ingin bergantung pada RubyGems untuk melakukannya, jadi saya menulis Versionkelas sederhana yang melakukan semua yang saya butuhkan: shorts.jeffkreeftmeijer.com/2014/…
jkreeftmeijer

Jawaban:

231
Gem::Version.new('0.4.1') > Gem::Version.new('0.10.1')
lebih kotor
sumber
14
The Gem::Version...sintaks membuat saya pikir saya akan perlu menginstal permata. Tapi itu tidak diperlukan.
Guillaume
Catatan: Ini memberikan kesalahan tentang variabel 'Permata' yang tidak ditentukan untuk saya di Ruby 1.x, tetapi berfungsi seperti yang diharapkan di Ruby 2.x. Dalam kasus saya, saya memeriksa RUBY_VERSION bukan Ruby 1.x (bukan 2.x), jadi saya hanya melakukan RUBY_VERSION.split ('.') [0] == "1" seperti John Hyland dan DigitalRoss melakukannya.
uliwitness
5
Gem::Dependency.new(nil, '~> 1.4.5').match?(nil, '1.4.6beta4')
levinalex
6
@uli, bukan Ruby 1. vs 2.; 1.8.x vs 1.9+. Ruby melalui 1.8.x tidak menyertakan rubygems secara default; Anda memerlukan require 'rubygems'untuk mendapatkan akses ke Gemnamespace. Dari 1.9, bagaimanapun, itu secara otomatis disertakan.
Mark Reed
Ini juga berfungsi untuk membandingkan versi NPM wildcard. +1
deepelement
35

Jika Anda perlu memeriksa batasan versi pesimis , Anda dapat menggunakan Gem :: Dependency seperti ini:

Gem::Dependency.new('', '~> 1.4.5').match?('', '1.4.6beta4')
levinalex.dll
sumber
1
Versi yang lebih baru tampaknya membutuhkan string untuk namanya. String kosong berfungsi dengan baik, yaituGem::Dependency.new('', '~> 1.4.5').match?('', '1.4.6beta4')
Peter Wagenet
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')]
DigitalRoss
sumber
3
Seperti beberapa jawaban lain di sini, sepertinya Anda melakukan perbandingan string dan bukan numerik, yang akan menyebabkan masalah saat membandingkan versi seperti '0,10' dan '0,4'.
John Hyland
7
Suara positif untuk solusi ringkas yang tidak memerlukan pemasangan permata.
JD.
2
Untuk apa nilainya: 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 Gumpalan kode membuatnya jelek, tetapi pada dasarnya, menggunakan ini vs Permata :: Versi sekitar dua kali lebih cepat.
Shai
Sebuah versi bukanlah sebuah array.
Sergio Tulentsev
15

Anda dapat menggunakan Versionomypermata (tersedia di 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
notnoop
sumber
4
Saya telah melihatnya, tetapi mengharuskan saya menggunakan 2 permata untuk melakukan hal yang sangat sederhana. Saya ingin menggunakannya sebagai pilihan terakhir.
pengguna239895
8
"Jangan menemukan kembali roda". Karena sederhana bukan berarti programmer tidak bekerja dan memikirkannya. Gunakan permata, baca kodenya, dan belajar darinya - dan lanjutkan ke hal-hal yang lebih besar dan lebih baik!
Trevoke
Manajemen ketergantungan dan pemeliharaan versi adalah masalah yang sulit, mungkin jauh lebih sulit daripada tugas membandingkan 2 versi. Saya sangat setuju bahwa memperkenalkan 2 dependensi lagi harus menjadi pilihan terakhir dalam kasus ini.
kkodev
10

Saya akan melakukannya

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

Maka Anda bisa melakukannya

a1 <=> a2

(dan mungkin semua perbandingan "biasa" lainnya).

... dan jika Anda menginginkan tes <atau >, Anda dapat melakukan mis

(a1 <=> a2) < 0

atau lakukan beberapa fungsi lainnya jika Anda ingin.

Carl Smotricz
sumber
1
Array.class_eval {include Comparable} akan membuat semua array merespon <,>, dll. Atau, jika Anda hanya ingin melakukan ini pada array tertentu: a = [1, 2]; a.extend (Comparable)
Wayne Conrad
4
Masalah yang saya temukan dengan solusi ini adalah ia mengembalikan bahwa "1.2.0" lebih besar dari "1.2"
Maria S
9

Gem::Version adalah cara mudah untuk pergi ke sini:

%w<0.1 0.2.1 0.44>.map {|v| Gem::Version.new v}.max.to_s
=> "0.44"
Mark Reed
sumber
Jauh lebih baik daripada versionomy yang membutuhkan ekstensi-c !?
W.Andrew Loe III
saya tidak berpikir 'max' akan bekerja .. itu akan melaporkan 0,5 lebih besar dari 0,44. Yang tidak benar saat membandingkan versi semver.
Flo Woo
2
ini tampaknya telah diperbaiki di Gem :: Version terbaru. 0,44 dilaporkan dengan benar sebagai lebih tinggi dari 0,5 sekarang.
Flo Woo
5

Jika Anda ingin melakukannya dengan tangan tanpa menggunakan permata apa pun, sesuatu seperti berikut ini akan berfungsi, meskipun kelihatannya agak buruk.

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

Pada dasarnya, Anda mengubah setiap string versi menjadi array bilangan bulat dan kemudian menggunakan operator perbandingan array . Anda dapat memecah langkah-langkah komponen untuk mendapatkan sesuatu yang sedikit lebih mudah diikuti jika ini masuk dalam kode yang perlu dipertahankan seseorang.

John Hyland
sumber
-1

Saya memiliki masalah yang sama, saya ingin komparator versi tanpa Permata, muncul dengan ini:

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
Wivlaro
sumber