I want to write a cmp
-like function which compares two version numbers and returns -1
, 0
, or 1
based on their compared va
Another solution:
def mycmp(v1, v2):
import itertools as it
f = lambda v: list(it.dropwhile(lambda x: x == 0, map(int, v.split('.'))[::-1]))[::-1]
return cmp(f(v1), f(v2))
One can use like this too:
import itertools as it
f = lambda v: list(it.dropwhile(lambda x: x == 0, map(int, v.split('.'))[::-1]))[::-1]
f(v1) < f(v2)
f(v1) == f(v2)
f(v1) > f(v2)
Remove trailing .0
and .00
with regex, split
and use cmp
function which compares arrays correctly:
def mycmp(v1,v2):
c1=map(int,re.sub('(\.0+)+\Z','',v1).split('.'))
c2=map(int,re.sub('(\.0+)+\Z','',v2).split('.'))
return cmp(c1,c2)
And, of course, you can convert it to a one-liner if you don't mind the long lines.
Remove the uninteresting part of the string (trailing zeroes and dots), and then compare the lists of numbers.
import re
def mycmp(version1, version2):
def normalize(v):
return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
return cmp(normalize(version1), normalize(version2))
This is the same approach as Pär Wieslander, but a bit more compact:
Here are some tests, thanks to "How to compare two strings in dot separated version format in Bash?":
assert mycmp("1", "1") == 0
assert mycmp("2.1", "2.2") < 0
assert mycmp("3.0.4.10", "3.0.4.2") > 0
assert mycmp("4.08", "4.08.01") < 0
assert mycmp("3.2.1.9.8144", "3.2") > 0
assert mycmp("3.2", "3.2.1.9.8144") < 0
assert mycmp("1.2", "2.1") < 0
assert mycmp("2.1", "1.2") > 0
assert mycmp("5.6.7", "5.6.7") == 0
assert mycmp("1.01.1", "1.1.1") == 0
assert mycmp("1.1.1", "1.01.1") == 0
assert mycmp("1", "1.0") == 0
assert mycmp("1.0", "1") == 0
assert mycmp("1.0", "1.0.1") < 0
assert mycmp("1.0.1", "1.0") > 0
assert mycmp("1.0.2.0", "1.0.2") == 0
def compare_version(v1, v2):
return cmp(*tuple(zip(*map(lambda x, y: (x or 0, y or 0),
[int(x) for x in v1.split('.')], [int(y) for y in v2.split('.')]))))
It's a one liner (split for legability). Not sure about readable...
Lists are comparable in Python, so if someone converts the strings representing the numbers into integers, the basic Python comparison can be used with success.
I needed to extend this approach a bit because I use Python3x where the cmp
function does not exist any more. I had to emulate cmp(a,b)
with (a > b) - (a < b)
. And, version numbers are not that clean at all, and can contain all kind of other alphanumeric characters. There are cases when the function can't tell the order so it returns False
(see the first example).
So I'm posting this even if the question is old and answered already, because it may save a few minutes in someone's life.
import re
def _preprocess(v, separator, ignorecase):
if ignorecase: v = v.lower()
return [int(x) if x.isdigit() else [int(y) if y.isdigit() else y for y in re.findall("\d+|[a-zA-Z]+", x)] for x in v.split(separator)]
def compare(a, b, separator = '.', ignorecase = True):
a = _preprocess(a, separator, ignorecase)
b = _preprocess(b, separator, ignorecase)
try:
return (a > b) - (a < b)
except:
return False
print(compare('1.0', 'beta13'))
print(compare('1.1.2', '1.1.2'))
print(compare('1.2.2', '1.1.2'))
print(compare('1.1.beta1', '1.1.beta2'))
My preferred solution:
Padding the string with extra zeroes and just using the four first is easy to understand, doesn't require any regex and the lambda is more or less readable. I use two lines for readability, for me elegance is short and simple.
def mycmp(version1,version2):
tup = lambda x: [int(y) for y in (x+'.0.0.0.0').split('.')][:4]
return cmp(tup(version1),tup(version2))