Làm thế nào để so sánh các phiên bản trong Ruby?


119

Làm thế nào để viết một đoạn mã để so sánh một số chuỗi phiên bản và nhận được phiên bản mới nhất?

Ví dụ chuỗi thích: '0.1', '0.2.1', '0.44'.


Tôi cần so sánh các ràng buộc phiên bản bi quan một thời gian trước, nhưng tôi không muốn phụ thuộc vào RubyGems để làm điều đó, vì vậy tôi đã viết một Versionlớp đơn giản thực hiện mọi thứ tôi cần: shorts.jeffkreeftmeijer.com/2014/…
jkreeftmeijer

Câu trả lời:


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

14
Các Gem::Version...cú pháp khiến tôi nghĩ tôi sẽ cần phải cài đặt một viên ngọc. Nhưng nó không được yêu cầu.
Guillaume

Lưu ý: Điều này gây ra lỗi về biến không xác định 'Gem' cho tôi trên Ruby 1.x, nhưng hoạt động như mong đợi trên Ruby 2.x. Trong trường hợp của tôi, tôi đang kiểm tra RUBY_VERSION xem có phải là Ruby 1.x (không phải 2.x) hay không, vì vậy tôi chỉ thực hiện RUBY_VERSION.split ('.') [0] == "1" như John Hyland và DigitalRoss.
uliwitness

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

6
@uliwitness nó không phải là Ruby 1.x vs 2.x; đó là 1.8.x so với 1.9+. Ruby đến 1.8.x không bao gồm rubyge theo mặc định; bạn cần require 'rubygems'có quyền truy cập vào Gemkhông gian tên. Tuy nhiên, từ ngày 1.9 trở đi, nó sẽ tự động được đưa vào.
Mark Reed

Điều này cũng hoạt động để so sánh các phiên bản NPM ký tự đại diện. +1
deepelement

35

Nếu bạn cần kiểm tra các ràng buộc phiên bản bi quan , bạn có thể sử dụng Gem :: Dependency như sau:

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

1
Các phiên bản mới hơn dường như yêu cầu một chuỗi cho tên. Một chuỗi rỗng hoạt động tốt, tức làGem::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
Giống như một số câu trả lời khác ở đây, có vẻ như bạn đang thực hiện so sánh chuỗi thay vì số, điều này sẽ gây ra sự cố khi so sánh các phiên bản như '0.10' và '0.4'.
John Hyland

7
Được ủng hộ cho giải pháp ngắn gọn không yêu cầu cài đặt đá quý.
JD.

2
Đối với những gì nó đáng giá: 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 đang làm cho nó trở nên xấu xí, nhưng về cơ bản, sử dụng phiên bản này so với Gem :: Nhanh hơn khoảng hai lần.
Shai

Tuy nhiên, một phiên bản không phải là một mảng.
Sergio Terrysev

15

Bạn có thể sử dụng Versionomyđá quý (có tại 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
Tôi đã thấy điều đó, nhưng yêu cầu tôi sử dụng 2 viên ngọc để làm một việc thực sự đơn giản. Tôi muốn sử dụng đó là lựa chọn cuối cùng.
user239895

8
"Đừng phát minh lại bánh xe". Bởi vì nó đơn giản không có nghĩa là lập trình viên không đặt công việc và suy nghĩ vào nó. Sử dụng đá quý, đọc mã và học hỏi từ nó - và chuyển sang những thứ lớn hơn và tốt hơn!
Trevoke

Quản lý phụ thuộc và bảo trì phiên bản là một bài toán khó, có lẽ khó hơn nhiều so với nhiệm vụ so sánh 2 phiên bản. Tôi hoàn toàn đồng ý rằng giới thiệu thêm 2 phụ thuộc nên là phương sách cuối cùng trong trường hợp này.
kkodev

10

tôi sẽ làm

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

Sau đó, bạn có thể làm

a1 <=> a2

(và có thể là tất cả những so sánh "thông thường" khác).

... và nếu bạn muốn có một <hoặc >kiểm tra, bạn có thể làm như

(a1 <=> a2) < 0

hoặc thực hiện một số gói chức năng khác nếu bạn đang muốn.


1
Array.class_eval {gồm có thể so sánh} sẽ làm cho tất cả các mảng phản hồi với <,>, v.v. Hoặc, nếu bạn chỉ muốn thực hiện việc này với một số mảng nhất định: a = [1, 2]; a.extend (tương đương)
Wayne Conrad

4
Vấn đề tôi thấy với giải pháp này là nó trả rằng "1.2.0" lớn hơn "1.2"
Maria S

9

Gem::Version là cách dễ dàng để đi đến đây:

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

Tốt hơn nhiều so với phép so sánh đòi hỏi phần mở rộng c !?
W. Andrew Loe III

tôi không nghĩ 'tối đa' sẽ hoạt động .. nó sẽ báo 0,5 lớn hơn 0,44. Điều này không đúng khi so sánh các phiên bản semver.
Flo Woo

2
điều này dường như đã được sửa trong Gem :: Phiên bản mới nhất. 0,44 được báo cáo chính xác là cao hơn 0,5 hiện tại.
Flo Woo

5

Nếu bạn muốn làm điều đó bằng tay mà không sử dụng bất kỳ viên đá quý nào, những thứ như sau sẽ hoạt động, mặc dù nó trông hơi nghiêm túc.

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

Về cơ bản, bạn chuyển từng chuỗi phiên bản thành một mảng số nguyên và sau đó sử dụng toán tử so sánh mảng . Bạn có thể chia nhỏ các bước thành phần để có thứ gì đó dễ làm theo hơn một chút nếu điều này xảy ra trong mã mà ai đó sẽ cần phải duy trì.


-1

Tôi đã gặp vấn đề tương tự, tôi muốn có một trình so sánh phiên bản không có Gem, đã nghĩ ra điều này:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.