# Compare version numbers

When we publish some software, we assign a version number to it. And users may want to update to the latest version of some software. So, it is the time to find out which version should be newer.

### Input

Input two version numbers as strings.

In the context of this challenge, we only support version numbers which are some digits joined by dots.

• A version number is a string which may only contain digits (0 ~ 9) and dots (.).
• Dots would not be the first / last character of a version number.
• There must be some digits between dots. No two dots may appear continuously.
• All numbers in a version number would be less than 216.

### Output

Compare the inputted version numbers and output whether first one is greater than / equals to / less than the second one. You are allowed to choose one of the following presentations:

• Use positive number / zero / negative number, while zero means equal;
• Use three constant distinct values;

### Comparing

You are not required to implement the algorithm described in this section. Your submission is valid as long as it result the same output with this algorithm.

• Version numbers are some decimal numbers joined by dots. We first split the two version numbers to arrays of numbers;
• Padding the ending of arrays with zeros to make them have same length;
• Compare from the first item to the last one:
• If the two array items different, the greater number means greater version number
• If they are the same, continue to compare the following items;
• If all items in the array are equal, the two versions are equal.

### Testcases

version1  version2  result
2         1         >
1.0.0     1         =
1.0       1.0.0     =
1.2.42    1.2.41    >
1.1.56789 1.2.0     <
1.10      1.2       >
1.20      1.150     <
18.04     18.4      =
7.010     7.8       >
1.0.0.1.0 1.00.00.2 <
00.00.01  0.0.0.1   >
0.0.1     0.1       <
42.0      4.2.0     >
999.999   999.999.1 <
2018.08.1 2018.08   >

• – FryAmTheEggman Aug 22 '18 at 4:20
• .NET has a Version object, but a single character isn't supported in it :( – Brian J Aug 22 '18 at 13:42
• @BrianJ and appending '.0' costs to many characters? :) – RobAu Aug 22 '18 at 14:22
• Well, it actually expects 2, 3, or 4 portions. So it fails on the 1.0.0.1.0 test case (though I did try your idea initially :) ) – Brian J Aug 22 '18 at 14:28
• I think Windows has a built-in that'll do this: StrCmpLogicalW – bace1000 Aug 23 '18 at 9:35

# Python 2, 8479 76 bytes

lambda*l:cmp(*map(lambda v:map(int,v.split('.')+[0]*len(l))[:len(l)],l))


Try it online!

Outputs -1,0,1 for <,=,>

# 05AB1E (legacy), 1514 13 bytes

'.¡0ζε.S}0K¬


Outputs -1 [] 1 for < = > respectively.

-1 byte thanks to @Emigna.

Explanation:

'.¡              # Split on dots
#  i.e. ['1.0.1.1.0','1.00.2.0']
#   → [['1','0','1','1','0'],['1','00','2','0']]
0ζ            # Zip, swapping rows and columns, using '0' as filler
#  i.e. [['1','0','1','1','0'],['1','00','2','0']]
#   → [['1','1'],['0','00'],['1','2'],['1','0'],['0','0']]
ε   }       # Map each:
#  Push both values to the stack
.S        #  And calculate the signum (1 if a>b; -1 if a<b; 0 if a==b)
#   i.e. [['1','1'],['0','00'],['1','2'],['1','0'],['0','0']]
#    → [0,0,-1,1,0]
0K     # Remove all zeros
#  i.e. [0,0,-1,1,0] → [-1,1]
¬    # Then take the head as result
#  i.e. [-1,1] → -1

• You can use 0K instead of ʒĀ}. – Emigna Aug 22 '18 at 8:41
• @Emigna Ah of course.. Thanks. – Kevin Cruijssen Aug 22 '18 at 9:04

# R, 32 bytes

rank(numeric_version(scan(,"")))


Try it online!

Using an R builtin

Outputs 1 2, 1.5 1.5, 2 1 for less, equal, greater.

Best so far, without builtin :

### R, 151 142 125 107 bytes

function(v,L=strsplit(v,'\\.'))Find(c,sign(Reduce('-',Map(as.double,Map(c,L,Map(rep,0,rev(lengths(L))))))))


Try it online!

Unrolled code with explanation :

function(v){             # character vector of 2 elements as function arg;
L=strsplit(v,'\\.')    # obtain a list of two character vectors
# with the separated version numbers;
R=rev(lengths(L))      # store in vector R the lengths of the 2 vectors and reverse it;
M1=Map(rep,0,R)        # create a list of 2 vector containing zeros
# repeated R[1] and R[2] times;
M2=Map(c,L,M1)         # append to the vectors in list L the zeros in M1;
M3=Map(as.double,M2)   # convert the character vectors in M2 to double;
w=sign(Reduce('-',M3)  # compute the sign of element by element difference M[[1]] - M[[2]]);
Find(c,w)            # returns the first non zero element in w, if none return NULL;
}
# N.B. as.double is necessary because "0XX" is interpreted as octal by strtoi unless
#      we use strtoi(x,10) which is exactly the same length of as.double(x)


Outputs -1, NULL, 1 for less, equal, greater.

Original concept, golfed down using sapply, [<- and %*%:

### R, 129 bytes

function(x,y=strsplit(x,"\\."),w=sign(sapply(y,function(x)strtoi("[<-"(rep(0,max(lengths(y))),seq(x),x),10))%*%c(1,-1)))w[!!w][1]


Try it online!

Now you have a list of two equal-length vectors of integers. Calculate the pairwise differences using Reduce and output the first non-zero element using the tricky little w[!!w][1] form at the end.

Outputs -1, NA, 1 for less, equal, greater.

• Impressive! Quick golf: extra newline at the end of your code - it should be 150 bytes ;) – JayCe Aug 22 '18 at 23:44
• reduce the number of named variables.... I feel there is a way to do it using a matrix instead of lists but I haven't found how to do it yet. – JayCe Aug 23 '18 at 1:21
• You can get this down to 100 bytes using scan function(a,b,d=scan(t=a,se='.'),e=scan(t=b,se='.'),f=1:max(lengths(list(d,e))),g=d[f]-e[f])g[!!g][1] (or 106 if you want to return -1,NA,1 not (negative),NA,(positive). – mnel Aug 23 '18 at 11:47
• @mnel the 100 byte solution needs a little work. It fails on the last two test cases. The padding has to be 0 and not (implictly) NA. I've made the answer a Community Wiki so whoever can fix it up can just add it. – ngm Aug 23 '18 at 14:24
• @digEmAll golfed 4 bytes by first calculating the sign , and then do Find(c,x). I think that's a new trick. – JayCe Aug 23 '18 at 20:30

# APL (Dyalog Unicode), 18 17 bytes

1 byte saved thanks to @Adám for using ⍤1 instead of ∘↑(...)¨ and by changing the input format from a nested array to a matrix

(⍋-⍒)(⍎¨∊∘⎕D⊆⊢)⍤1


Try it online!

Takes the input as a matrix of chars as the right argument, where each version string is on its own row. Outputs ¯1 1, 0 0, 1 ¯1 for <, =, > respectively.

(⍎¨∊∘⎕D⊆⊢)⍤1 on each row

• ∊∘⎕D⊆⊢ group all occurrences of digits, that is, split on .

• ⍎¨ and convert each of these occurrences to a number

↑ convert to a matrix, where the first input is on the top row and the second one in the bottom, padding with 0s where necessary

(⍋-⍒) and

• - subtract
• ⍒ the indices into the rows which would sort them in descending order
• ⍋ same as the top but for ascending order

# Perl 6, 63 47 22 bytes

{"v$^a cmp v$^b".EVAL}


Try it online!

Turns out that Perl 6 has a version type that pretty much fits the description. This is an anonymous code block that takes a list of two version strings and returns either More,Same or Less.

### Explanation:

{                    }  # Anonymous code block
"             "        # Create a string of code
v$^a cmp v$^b         # Comparing the two versions
.EVAL   # And EVAL it


## Or, without built-in types for 47 bytes:

{first +*,[Z<=>] map *.split('.')[^@_.ords],@_}


Try it online!

Anonymous code block that takes two strings and returns More if the second is greater, Less if the second is smaller and Nil if they are equal.

### Explanation:

{                                             } # Anonymous code block
map *.split('.')          ,@_  # Split both strings by '.'
[^@_.ords]     # Pad the lists by a lot
[Z<=>]   # Zip the strings with the <=> operator
first +*,  # Get the first value that when coerced to an int, is not 0


# Brachylog, 49 40 bytes

+0|{~c[H,".",T]hị;T|ị;0|0}ᵐz{h-0&t↰₀|h-}


...It's still rather unimpressively lengthy.

Expects a list of two strings. Uses positive number / zero / negative number as > / = / <.

Try it online!

## Explanation

Splitting the inputs

Given an input that does not unify with [0, 0], such as ["1.02.0", "1.2.0.1.0"], the below segment outputs, e.g., [[1, "02.0"], [1, "2.0.1.0"]].

                            # unify the input with...
+0                          # : a list whose sum = 0 (output is 0)
|{                     }ᵐ # : OR a list that when mapped...
~c                      # : : if the input string unifies with a list of the form...
[H,".",T]             # : : : e.g. "1.02.0", H = "1", T = "02.0"
hị           # : : : coerce the head to an integer
;T         # : : : append the string T
# : : : "1.02.0" -> [1, "02.0"]
|ị       # : : OR it unifies with an integer
;0     # : : : append 0
# : : : "1" -> [1, 0]
|0   # : : OR it unifies with 0
# : : : 0 -> [0]


Comparing the inputs

Given, e.g., [[1, "02.0"], [1, "2.0.1.0"]], zips the sublists into [[1, 1], ["02.0", "2.0.1.0"]] and compares the values in the head ([1,1]). Recur on the second sublist. Note that the zip predicate z cycles through shorter lists so that zipping with [0,0] is equivalent to zipping with [0], hence the previous step unifies 0 with 0 without further values appended.

z             # zip the sublists
{          } # unify the result (r) with...
h           # : take the head of the result
-          # : : subtract the second value from the first
0         # : : if the difference unifies with 0...
&t↰₀     # : : recur on the tail of r
|h-  # : OR unify with the difference of the elements of the head
# : (equivalent to returning early)


# JavaScript (ES6), 73 68 bytes

Saved 5 bytes thanks to @redundancy

Takes input as (a)(b). Returns $0$ for equal, a positive integer for greater than or a negative integer for less than.

a=>b=>(a+[...b].fill.).split..some((x,i)=>d=~b.split.[i]-~x)*d


Try it online!

• Nice. If I've understood correctly, you can save bytes by substituting replace with fill. The operands for - are swapped since both must now be coerced to a number. Try it online! – redundancy Aug 24 '18 at 16:53
• @redundancy Good idea! (Not sure if my implementation exactly is what you had in mind, though.) – Arnauld Aug 26 '18 at 11:52
• I assumed your intent was to append enough values coercible to 0 such that mapping over the substrings of a eventually cycles through those 0 values if b contains more number segments than a. It so happens that the shortest method of ensuring that is so is to split over a b-length string of '.' by leveraging the existing split applied to a. – redundancy Aug 26 '18 at 19:37

# Java (JDK 10), 20196 89 bytes

java.util.Comparator.comparing(java.lang.module.ModuleDescriptor.Version::parse)::compare


Try it online!

Returns a negative number if the first version is smaller than the second one, a positive one if the first version is greater than the second one and 0 if they're equal.

Yep, that's some heavy work to "just" call a built-in!

## Credits

• I tried, but I'm only able to remove three bytes.. 228 bytes – Kevin Cruijssen Aug 22 '18 at 12:04
• Found something more: 217 bytes – Kevin Cruijssen Aug 22 '18 at 12:15
• That's probably it.. Already tried try-finally so the if-check can be simplified; tried return inside the loop if t!=0; tried using Integer and i.compare(i.valueOf(...),i.valueOf(...)); tried using generics like this <T>T[]g(T s){return(T[])(s+"").replaceAll("(\\.0+)*$","").split("\\.");}; etc. All are 2-6 bytes longer. If you (or anyone else) does find something more, let me know please. Curious to know what. :) – Kevin Cruijssen Aug 22 '18 at 12:25 • @KevinCruijssen No, I can't because "All numbers in a version number would be less than 2^16." Short ranges from -(2^15) to 2^15-1. – Olivier Grégoire Aug 22 '18 at 15:00 • @KevinCruijssen I could remove 105 bytes! How? Well, I found a built-in ;) – Olivier Grégoire Sep 1 '18 at 21:26 # Python 2, 87 bytes lambda*p:cmp(*zip(*map(lambda x,y:(x or 0,y or 0),*[map(int,u.split('.'))for u in p])))  Try it online! Outputs -1,0,1 for <,=,>, respectively. # Retina 0.8.2, 54 bytes \d+$*
+^(.)(.*=)\1
$2 (.*=|^=.*)1.* < .*1.*=.* > \.  Try it online! Link includes test cases. Uses the separator value as the equality output, so for convenience the header converts the input separator to = but it could be anything not in [.\d]. Explanation: \d+$*


Convert to unary.

+^(.)(.*=)\1
$2  Repeatedly delete the first character from each side until they differ or one side runs out. This is much faster than trying to match prefixes, although possibly not golfier. At this point, the strings are in one of several forms, which need to be decoded to a comparison result. 1. If neither string contains a 1 then the result is = 2. If the left string starts with a 1 then the result is > 3. If the right string starts with a 1 then the result is < 4. If the left string is empty then the result is < 5. At this point the right string is empty so the result is > Another way of thinking about this is that if one string contains a 1 and the other does not start with a 1 then that string is greater, however that turns out to be a byte longer. (.*=|^=.*)1.* <  Check for case 3, or case 4 without case 1. .*1.*=.* >  If the left string still contains a 1 at this point then it is greater. \.  Otherwise delete any left over .s. # Firefox Browser Console REPL, 19 bytes Services.vc.compare  I believe this internal function performs the required comparison. It returns -1, 0, or 1. • I would suggest you post the Firefox chrome code as another answer... – tsh Aug 22 '18 at 12:22 • btw, I'm not sure how Firefox chrome code count its bytes. Should Cu.import("resource://gre/modules/Services.jsm"); be counted? – tsh Aug 22 '18 at 12:26 • @tsh That's why I added "Browser Console REPL"... – Neil Aug 22 '18 at 12:28 # PHP, 38 bytes <?=version_compare($argv[1],$argv[2]);  Outputs -1 → < | 0 → = | 1 → > Try it online! • I think your submission can just be the function itself – Jo King Aug 24 '18 at 2:43 • This returns the wrong result for any pair of inputs that differ in trailing zeroes only, e.g. 1.0.0 and 1 – oktupol Aug 24 '18 at 7:22 # C (gcc), 140 134 bytes This code outputs a negative, 0 or a positive for <,= or > respectively. i;n;p;q;g(char*s){for(i=n=0;*s&&++n&&*s-46;i=i*10+*s++-48);i=i;}f(char*a,char*b){for(p=q=0;*a+*b&&p==q;b+=n)p=g(a),a+=n,q=g(b);a=p-q;}  Try it online! # Edits: • Saved 6 bytes thanks to ceilingcat ! • The challenge states: "Use three constant distinct values;" Your code doesn't return constants. – Olivier Grégoire Aug 22 '18 at 14:03 • @Olivier It states I can "Use three constant distinct values;" OR "Use positive number / zero / negative number, while zero means equal;" – Annyo Aug 22 '18 at 14:09 • My bad! You're correct. – Olivier Grégoire Aug 22 '18 at 14:15 # Ruby, 75 bytes ->a,b{c=d=0;c,d=[a,b].map{|f|f.slice!(/\d*./).to_i}while''<a+b&&c==d;c<=>d}  Try it online! # JavaScript (Node.js), 10588 80 bytes -17 bytes from @redundancy. Wow! -8 bytes removing Math.sign. Thanks @tsh Returns a negative, zero or positive value f=(a,b,r=/(\d*).?(.*)/)=>a+b&&+((a=r.exec(a))[1]-(b=r.exec(b))[1]||f(a[2],b[2]))  Try it online! • 88 bytes using exec to split strings. Try it online! – redundancy Aug 23 '18 at 18:47 • @redundancy Damn, thanks! thats a pretty cool trick – Luis felipe De jesus Munoz Aug 23 '18 at 18:56 • Maybe you want to remove the Math.sign to save some bytes by switching to positive / zero / negative values. And maybe a positive sign is required. – tsh Aug 24 '18 at 2:55 # Japt, 16 11 bytes -5 bytes from @Shaggy Outputs: • negative number for < • (null or 0) for = • positive number for > N®q.Ãy_r-Ãf  Try it online! • Would this work? – Shaggy Aug 24 '18 at 14:22 • @Shaggy Yep, It could be shorten to 10 bytes outputing negative, null or 0, positive for < = > respectively but I dont know if the input can be taken as an array – Luis felipe De jesus Munoz Aug 24 '18 at 14:36 # Clean, 116 111 bytes import StdEnv,Text ?s=map toInt(split"."s)$a b= @(?a)(?b)
@[h:t][u:v]|h==u= @t v=h-u
@l[]=sum l
@[]l= ~(sum l)


Try it online!

Outputs a negative number when the first argument is less than the second one, zero when they're equivalent, and a positive number when it is more than the second one.

# Swift 4, 155 bytes

### Header (not counted: the code is non-recursive):

let f:(String,String)->Bool? =


### Code

{let x:(String)->[Int]={$0.split{$0=="."}.map{Int($0)!}.reversed().drop{$0==0}.reversed()},a=x($0),b=x($1)
return a==b ?nil:a.lexicographicallyPrecedes(b)}


Try it online!

### Explanations

• We trim trailing .0.
• We compare components numerically.

• nil for =
• true for <
• false for >

# JavaScript 64 bytes

a=>b=>(e=i=>(g=v=>v.split.[i]||0)(a)-g(b)||!a[i]-1&&e(i+1))(0)


Try it online!

a=>b=>(                            // Main function takes arguments like ("1.2.42")("1.2.41")
e=i=>                          // e(i) compares the ith number, returns >0, <0 or =0.
(   g=v=>v.split.[i]||0  // g() returns the ith string or 0
)(a)                       // call g(a)
-g(b)                      // subtracting g(b) from g(a) casts strings to integer
||                         // If they are not equal return result now
!a[i]-1 &&                 // recursion limited to a.length, always sufficient
e(i+1)                     // next i


# Perl 5, 55 bytes

sub c{@_<2?sprintf"%9d"x9,split/\./,pop:-(c(pop)cmp c(pop))}


Try it online!

## Burlesque - 17 bytes

wd{'.;;)ri}m[^pcm

blsq ) "2018.08.1 2018.08"wd{'.;;)ri}m[^pcm
1
blsq ) "0.0.1 0.1"wd{'.;;)ri}m[^pcm
-1
blsq ) "1.1.56789 1.2.0"wd{'.;;)ri}m[^pcm
-1


If you want output in '><=' then add ?i"<=>"j!!Q.

# Powershell, 88 bytes

Returns 0 for equal, a positive integer for greater than or a negative integer for less than.

param($a,$b)+(($x=$a-split'\.')+($y=$b-split'\.')|%{$x[+$i]-$y[$i++]}|?{$_}|Select -f 1)  Less golfed test script: $f = {

param($a,$b)
$x=$a-split'\.'
$y=$b-split'\.'
$z=$x+$y|%{$x[+$i]-$y[$i++] }|?{$_}|Select -first 1
+$z # convert$null to 0

}

@(
,("2"         ,"1"         , 1)
,("1.0.0"     ,"1"         , 0)
,("1.0"       ,"1.0.0"     , 0)
,("1.2.42"    ,"1.2.41"    , 1)
,("1.1.56789" ,"1.2.0"     ,-1)
,("1.10"      ,"1.2"       , 1)
,("1.20"      ,"1.150"     ,-1)
,("18.04"     ,"18.4"      , 0)
,("7.010"     ,"7.8"       , 1)
,("1.0.0.1.0" ,"1.00.00.2" ,-1)
,("00.00.01"  ,"0.0.0.1"   , 1)
,("0.0.1"     ,"0.1"       ,-1)
,("42.0"      ,"4.2.0"     , 1)
,("999.999"   ,"999.999.1" ,-1)
,("2018.08.1" ,"2018.08"   , 1)
) | % {
$v1,$v2,$expected =$_
$result = &$f $v1$v2
"$([Math]::Sign($result)-eq$expected):$result"
}


Output:

True: 1
True: 0
True: 0
True: 1
True: -1
True: 8
True: -130
True: 0
True: 2
True: -1
True: 1
True: -1
True: 38
True: -1
True: 1


# Dart, 277 231 bytes

F(s,{t}){t=s.split('.').map(int.parse).toList();while(t.last<1)t.removeLast();return t;}f(a,b,{d,e,f,g,h,i=0}){d=F(b);e=F(a);g=d.length;h=e.length;f=h>g?g:h;for(;i<f;i++)if(e[i]!=d[i])return e[i]>d[i]?1:-1;return h>g?1:(h<g?-1:0);}


Try it online!

• -44 bytes by using variables to store length and using ternary in loop
• -2 bytes by removing the for brackets

# Swift 4+Foundation, 160 bytes (142+18), 155 bytes (142+13)

### Import (13 bytes, including ; to separate from the code):

This will import Foundation, but is 5 bytes shorter than import Foundation.

import UIKit;


### Header (not counted: the code is non-recursive):

let f:(String,String)->ComparisonResult =


### Code (142 bytes):

{var x={($0 as String).split{$0=="."}.count},a=$0,b=$1
while x(a)<x(b){a+=".0"}
while x(b)<x(a){b+=".0"}
return a.compare(b,options:.numeric)}


Try it online!

### Explanations

1. We append some trailing .0 for same number of components.
2. We compare components numerically.

### Returned constants

• ComparisonResult.orderedSame for =
• ComparisonResult.orderedAscending for <
• ComparisonResult.orderedDescending for >
• I'm not sure if we count the import statement, so I posted a separate answer that doesn't require Foundation and with a bytes count in-between 142 bytes (not counting import) and 160 bytes (counting import). – Cœur Aug 23 '18 at 20:15

# Zsh, 54 bytes

eval {autoload,}' is-at-least $'{1\$2,2\ $1}';<<<$?;'


This evals to the following eight statements:

autoload is-at-least $1$2     # loads the "is-at-least" function
<<<$? # success, prints 0 autoload is-at-least$2 $1 # redundant <<<$?                          # success, prints 0
is-at-least $1$2              # exits 1 if $1 <$2
<<<$? is-at-least$2 $1 # exits 1 if$2 < $1 <<<$?


So the three unique values are:

 cmp |  value
-----+------------------------------------------
=  |  0<newline>0<newline>0<newline>0<newline>
<  |  0<newline>0<newline>1<newline>0<newline>
>  |  0<newline>0<newline>0<newline>1<newline>