วิธีเปรียบเทียบเวอร์ชันใน Ruby


120

จะเขียนโค้ดเพื่อเปรียบเทียบสตริงบางเวอร์ชันและรับโค้ดใหม่ล่าสุดได้อย่างไร

ตัวอย่างเช่นสตริง: '0.1', '0.2.1', '0.44'.


ฉันต้องการเปรียบเทียบข้อ จำกัด ของเวอร์ชันในแง่ร้ายในขณะที่ย้อนกลับไป แต่ฉันไม่ต้องการพึ่งพา RubyGems ในการทำดังนั้นฉันจึงเขียนVersionคลาสง่ายๆที่ทำทุกอย่างที่ฉันต้องการ: shorts.jeffkreeftmeijer.com/2014/…
jkreeftmeijer

คำตอบ:


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

14
Gem::Version...ไวยากรณ์ทำให้ฉันคิดว่าฉันจะต้องติดตั้งอัญมณี แต่มันไม่จำเป็น
Guillaume

หมายเหตุ: สิ่งนี้ทำให้เกิดข้อผิดพลาดเกี่ยวกับตัวแปรที่ไม่ได้กำหนด 'Gem' สำหรับฉันใน Ruby 1.x แต่ทำงานได้ตามที่คาดไว้บน Ruby 2.x ในกรณีของฉันฉันตรวจสอบ RUBY_VERSION ว่าเป็น Ruby 1.x (ไม่ใช่ 2.x) ดังนั้นฉันจึงทำ RUBY_VERSION.split ('.') [0] == "1" เช่น John Hyland และ DigitalRoss
uliwitness

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

6
@uliwitness ไม่ใช่ Ruby 1.x vs 2.x; มันคือ 1.8.x เทียบกับ 1.9+ Ruby ถึง 1.8.x จะไม่รวม Ruby โดยค่าเริ่มต้น คุณต้องมีrequire 'rubygems'เพื่อเข้าถึงGemเนมสเปซ อย่างไรก็ตามตั้งแต่ 1.9 เป็นต้นไปจะรวมไว้โดยอัตโนมัติ
Mark Reed

สิ่งนี้ใช้ได้กับการเปรียบเทียบเวอร์ชัน NPM ที่เป็นสัญลักษณ์แทนด้วย +1
deepelement

35

หากคุณต้องการตรวจสอบข้อ จำกัด ของเวอร์ชันในแง่ร้ายคุณสามารถใช้Gem :: Dependency ได้ดังนี้:

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

1
เวอร์ชันที่ใหม่กว่าดูเหมือนจะต้องใช้สตริงสำหรับชื่อ สตริงว่างทำงานได้ดีเช่น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
เช่นเดียวกับคำตอบอื่น ๆ ที่นี่ดูเหมือนว่าคุณกำลังทำการเปรียบเทียบสตริงแทนที่จะเป็นตัวเลขซึ่งจะทำให้เกิดปัญหาเมื่อเปรียบเทียบเวอร์ชันเช่น '0.10' และ '0.4'
John Hyland

7
ได้รับการโหวตให้เป็นโซลูชันที่รัดกุมซึ่งไม่จำเป็นต้องติดตั้งอัญมณี
JD.

2
สำหรับสิ่งที่คุ้มค่า: 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 ทำให้น่าเกลียด แต่โดยพื้นฐานแล้วการใช้เวอร์ชันนี้ vs Gem :: จะเร็วกว่าประมาณสองเท่า
Shai

อย่างไรก็ตามเวอร์ชันไม่ใช่อาร์เรย์
Sergio Tulentsev

15

คุณสามารถใช้Versionomyอัญมณี (มีให้ที่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
ฉันเคยเห็นแบบนั้น แต่ต้องการให้ฉันใช้อัญมณี 2 เม็ดเพื่อทำสิ่งที่ง่ายจริงๆ ฉันต้องการใช้สิ่งนั้นเป็นทางเลือกสุดท้าย
user239895

8
"อย่าสร้างล้อใหม่". เพราะมันง่ายไม่ได้หมายความว่าโปรแกรมเมอร์ไม่ได้ทำงานและคิดลงไป ใช้อัญมณีอ่านโค้ดและเรียนรู้จากมัน - และก้าวไปสู่สิ่งที่ใหญ่กว่าและดีกว่า!
Trevoke

การจัดการการพึ่งพาและการบำรุงรักษาเวอร์ชันเป็นปัญหาที่ยากซึ่งอาจจะยากกว่างานเปรียบเทียบ 2 เวอร์ชัน ฉันยอมรับโดยสิ้นเชิงว่าการแนะนำการอ้างอิงอีก 2 รายการควรเป็นทางเลือกสุดท้ายในกรณีนี้
kkodev

10

ฉันจะทำ

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

จากนั้นคุณสามารถทำได้

a1 <=> a2

(และอาจเป็นการเปรียบเทียบแบบ "ปกติ" อื่น ๆ ทั้งหมด)

... และถ้าคุณต้องการ<หรือ>ทดสอบคุณสามารถทำได้เช่น

(a1 <=> a2) < 0

หรือทำการตัดฟังก์ชันเพิ่มเติมหากคุณมีความโน้มเอียงมาก


1
Array.class_eval {include Comparable} จะทำให้อาร์เรย์ทั้งหมดตอบสนองต่อ <,> ฯลฯ หรือหากคุณต้องการทำสิ่งนี้กับอาร์เรย์บางรายการ: a = [1, 2]; ก. ขยาย (เทียบเคียง)
Wayne Conrad

4
ปัญหาที่ฉันพบในการแก้ปัญหานี้คือมันส่งกลับว่า "1.2.0" ใหญ่กว่า "1.2"
Maria S

9

Gem::Version เป็นวิธีง่ายๆที่จะไปที่นี่:

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

ดีกว่า versionomy ที่ต้องใช้ c-extension เยอะ!?
W. Andrew Loe III

ฉันไม่คิดว่า 'max' จะใช้งานได้ .. มันจะรายงาน 0.5 เป็นมากกว่า 0.44 ซึ่งไม่เป็นความจริงเมื่อเปรียบเทียบเวอร์ชัน semver
Flo Woo

2
ดูเหมือนว่าจะได้รับการแก้ไขแล้วใน Gem :: Version ล่าสุด 0.44 ได้รับการรายงานอย่างถูกต้องว่าสูงกว่า 0.5 ในขณะนี้
Flo Woo

5

หากคุณต้องการทำด้วยมือโดยไม่ต้องใช้อัญมณีใด ๆ สิ่งต่อไปนี้ควรใช้งานได้แม้ว่าจะดูเรียบง่าย

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

เป็นหลักคุณเปิดแต่ละสายรุ่นในอาร์เรย์ของจำนวนเต็มแล้วใช้ตัวดำเนินการเปรียบเทียบอาร์เรย์ คุณสามารถแยกขั้นตอนส่วนประกอบออกเพื่อให้ทำตามได้ง่ายขึ้นเล็กน้อยหากสิ่งนี้เกิดขึ้นในโค้ดที่ใครบางคนต้องดูแล


-1

ฉันมีปัญหาเดียวกันฉันต้องการตัวเปรียบเทียบเวอร์ชัน Gem-less มาพร้อมกับสิ่งนี้:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.