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'.


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

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

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

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

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.


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"

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.


-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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.