Skip to content

Instantly share code, notes, and snippets.

@arlimus
Last active August 29, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arlimus/10843886 to your computer and use it in GitHub Desktop.
Save arlimus/10843886 to your computer and use it in GitHub Desktop.
Compare package versions. Correctly handles numbers and letters.
# usage: provide a list (Array) of package objects
# package.version must contain the version string, e.g. "1.0.0e-1ubuntu3.4"
# Method 1: simple
# blow up all numbers
def sort_packages list
list.map do |x|
[ x, x.version.gsub(/[0-9]+/){|z| "%010x" % z} ]
end.
sort{|x,y| x[1] <=> y[1]}.
map{|x| x[0]}
end
# Method 2: copmlex
# split into pieces and compare
def sort_packages list
# map the list into a structure of { package -> version }
# where version is already split into all its small components and integeres
comparable = list.map{|x| [ x, split_version(x.version) ] }
# now we have entries of [package, [ar_1, ar_2]]
# sort them via ar_1; if they are equal (i.e. == 0), then sort by the second part
list = comparable.sort do |x,y|
c1 = compare_version_parts( x[1][0], y[1][0] )
if (c1 == 0)
compare_version_parts( x[1][1], y[1][1] )
else
c1
end
end
end
def split_version version
# split the version in the middle where 'ubuntu' occurs
# eg: "1.0.0e-3ubuntu1" -> a = "1.0.0e-3", b = "1"
a,b = version.split('ubuntu')
# make sure we got strings; if e.g. ubuntu doesn't exist
# we will get b = nil; nil.to_s = '', so that will allow us to continue
ar = a.to_s
br = b.to_s
# split into components
# will create groups of integers and strings on boundaries
# e.g.
as = ar.scan(/(?![^0-9])[0-9]+|(?![^a-z])[a-z]+/).map{|i| (i.match(/[0-9]/).nil?) ? i : i.to_i }
bs = br.scan(/(?![^0-9])[0-9]+|(?![^a-z])[a-z]+/).map{|i| (i.match(/[0-9]/).nil?) ? i : i.to_i }
# return the split version
[as,bs]
end
# returns nil if both versions are equal
# return false if a > b
# return true if a < b
def compare_version_parts parts_a, parts_b
a = parts_a[0]
b = parts_b[0]
# take away when anything is nil
return 0 if a.nil? and b.nil?
return -1 if b.nil?
return 1 if a.nil?
# in case they are equal
return compare_version_parts parts_a[1..-1], parts_b[1..-1] if a == b
# in case they are different
# if both are integers
if a.is_a?(Integer) and b.is_a?(Integer)
return ( a < b ) ? -1 : 1
end
# if one is an integer; integer wins over non-integer
return 1 if a.is_a?(Integer)
return -1 if b.is_a?(Integer)
# simply compare strings
return ( a < b ) ? -1 : 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment