| Bytes | Lang | Time | Link |
|---|---|---|---|
| 123 | Bash | 240712T153832Z | Themooni |
| 012 | 05AB1E legacy | 180822T073359Z | Kevin Cr |
| 041 | Perl 5 + pa | 240712T115036Z | Dom Hast |
| 064 | JavaScript Node.js | 240709T082501Z | l4m2 |
| 248 | Go | 240708T155538Z | bigyihsu |
| 050 | Perl 5 plF\. | 210117T005004Z | Xcali |
| 011 | Jelly | 210116T215142Z | caird co |
| 050 | Zsh | 191016T100254Z | GammaFun |
| nan | Swift 4+Foundation | 180823T142929Z | Cœur |
| 231 | Dart | 181204T144607Z | Elcan |
| 022 | Perl 6 | 180822T065554Z | Jo King |
| 088 | Powershell | 181204T111312Z | mazzy |
| 017 | Burlesque | 181203T123013Z | mroman |
| 089 | Java JDK 10 | 180822T090939Z | Olivier |
| 134 | C gcc | 180822T131215Z | Annyo |
| 055 | Perl 5 | 180827T093625Z | Kjetil S |
| 017 | APL Dyalog Unicode | 180822T093152Z | user4180 |
| 068 | JavaScript ES6 | 180824T091756Z | Arnauld |
| 040 | Brachylog | 180822T232024Z | redundan |
| 011 | Japt | 180823T125403Z | Luis fel |
| 032 | R | 180822T205031Z | ngm |
| 080 | JavaScript Node.js | 180823T171859Z | Luis fel |
| 155 | Swift 4 | 180823T192517Z | Cœur |
| 111 | Clean | 180822T051149Z | Οurous |
| 075 | Ruby | 180822T131400Z | G B |
| 054 | Retina 0.8.2 | 180822T092143Z | Neil |
| 076 | Python 2 | 180822T065251Z | TFeld |
| 087 | Python 2 | 180822T061045Z | Chas Bro |
Bash, 123 bytes
c='grep -Po .*[1-9]0*(?=\.?)'
u=`$c<<<$1`
e=$u$'\n'`$c<<<$2`
sort -sCV<<<$e$'\n'$u&&echo e||(sort -CV<<<$e&&echo l||echo g)
uses GNU sort which can sort version numbers using the -V flag.
explanation:
c=' ' # the grep command will run twice, so we alias it
grep -Po .*[1-9]0?(?=\.?) # this regex matches everything until the first dot before any eventual trailing .0's (fixes 1.1.0 > 1.1)
u=`$c<<<$1` # apply the filter
e=$u$'\n'`$c<<<$2` #we are going to use GNU sort to test version numbers, so we build a "file" that can be sorted, with each version on a line
sort -sCV<<<$e$'\n'$u #test if versions $1 >= $2 >= $1, testing for equality. -s allows 1.01 == 1.1
&&echo e||( ) #print e and exit if equal
sort -CV<<<$e # -C: test if already sorted, -V: sort with version numbers
&&echo l||echo g #print output accordingly
05AB1E (legacy), 15 14 13 12 bytes
'.¡0ζø`.S0Kн
Outputs -1 [] 1 for < = > respectively.
-1 byte thanks to @Emigna.
Try it online or verify all test cases.
Explanation:
# e.g. input = ['1.0.1.1.0','1.00.2.0']
'.¡ # Split each inner list of the (implicit) input on dots
# → [['1','0','1','1','0'],['1','00','2','0']]
0ζ # Zip/transpose, swapping rows and columns, using '0' as filler
# → [['1','1'],['0','00'],['1','2'],['1','0'],['0','0']]
ø # Zip/transpose back again
# → [['1','0','1','1','0'],['1','00','2','0','0']]
` # Pop and push both lists to the stack
.S # Calculate the signum of the values at the same positions
# (1 if a>b; -1 if a<b; 0 if a==b)
# → [0,0,-1,1,0]
0K # Remove all zeros
# → [-1,1]
н # Pop and leave the first item
# -1
# (after which it is output implicitly as result)
Perl 5 + -pa, 41 bytes
Returns 1 for >, 0 for = and -1 for <.
$_=eval join" cmp ",map{v.s/(\.0)+$//r}@F
Explanation
evaluates the result of cmparing the two inputs after stripping trailing .0s and prepending vs - which forces the number to be a v-string.
Perl 5 + -0513p, 41 bytes
s/(\.0)+$//gm;s/^/v/mg;s/
/ cmp /;$_=eval
Explanation
Similar to the above, but relies on newlines to replace .0s and add the vs.
JavaScript (Node.js), 64 bytes
a=>b=>(g=i=>(a+b)[i]?~a.split`.`[i]-~b.split`.`[i]||g(i+1):0)(0)
Go, 248 bytes
import(."fmt";."strings")
func f(a,b string)int{S,C,c,Z:=Split,Sscanf,"%d","0"
A,B:=S(a,"."),S(b,".")
for len(A)<len(B){A=append(A,Z)}
for len(A)>len(B){B=append(B,Z)}
for i:=range A{m,n:=0,0
C(A[i],c,&m)
C(B[i],c,&n)
if m!=n{return m-n}}
return 0}
Returns a negative, zero, or positive number for < = > respectively.
Explanation
import(."fmt";."strings")
func f(a,b string)int{S,C,c,Z:=Split,Sscanf,"%d","0"
A,B:=S(a,"."),S(b,".") // split each version into their fields
for len(A)<len(B){A=append(A,Z)} // extend with 0s until the number of fields match up
for len(A)>len(B){B=append(B,Z)} // ^^^
for i:=range A{m,n:=0,0 // checking the version fields:
C(A[i],c,&m);C(B[i],c,&n) // coerce into an int
if m!=n{return m-n}} // if they are not equal, exit immediately
return 0} // return 0 if we get to the end; means they are the same version
Perl 5 -plF\., 50 bytes
@b=<>=~/\d+/g;1while@F+@b&&!($_=shift@F<=>shift@b)
Takes input on two lines.
| Output | Meaning |
|---|---|
| 1 | First version is greater |
| 0 | Versions are equal |
| -1 | Second version is greater |
Jelly, 11 bytes
ṣ€”.Vz0I¬ÞḢ
May be stretching the rules a little, but going by this I/O default, it's allowed. This uses the output presentation:
Use positive number / zero / negative number, while zero means equal;
but the output is wrapped as a singleton array
How it works
ṣ€”.Vz0I¬ÞḢ - Main link. Takes a pair of versions [a, b] on the left
€ - Over each:
ṣ ”. - Split on "."
V - Evaluate each numeric string to a number
z0 - Transpose, filling with 0s
I - Take the differences of each pair
¬Þ - Sort by logical NOT
This shuffles 0 to the end and preserves the order of everything else
Ḣ - Take the first element
Zsh, 54 50 bytes
-4 bytes by using $@ instead of $1\ $2
eval {autoload,}' is-at-least $'{@,2\ $1}';<<<$?;'
This evals to the following eight statements:
autoload is-at-least $@ # loads the "is-at-least" function
<<<$? # success, prints 0
autoload is-at-least $2 $1 # redundant
<<<$? # success, prints 0
is-at-least $@ # 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>
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)}
Explanations
- We append some trailing .0 for same number of components.
- We compare components numerically.
Returned constants
- ComparisonResult.orderedSame for =
- ComparisonResult.orderedAscending for <
- ComparisonResult.orderedDescending for >
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);}
- -44 bytes by using variables to store length and using ternary in loop
- -2 bytes by removing the for brackets
Perl 6, 63 47 22 bytes
{"v$^a cmp v$^b".EVAL}
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],@_}
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
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
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.
Java (JDK 10), 201 96 89 bytes
java.util.Comparator.comparing(java.lang.module.ModuleDescriptor.Version::parse)::compare
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
- -14 bytes, thanks to Kevin Cruijssen
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;}
Edits:
- Saved 6 bytes thanks to ceilingcat !
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
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
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
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 > / = / <.
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)
Japt, 16 11 bytes
-5 bytes from @Shaggy
Outputs:
- negative number for
< - (
nullor0) for= - positive number for
>
N®q.Ãy_r-Ãf
R, 32 bytes
rank(numeric_version(scan(,"")))
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))))))))
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]
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.
JavaScript (Node.js), 105 88 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]))
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)}
Explanations
- We trim trailing .0.
- We compare components numerically.
Returned constants
- nil for =
- true for <
- false for >
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)
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.
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}
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.
- If neither string contains a
1then the result is= - If the left string starts with a
1then the result is> - If the right string starts with a
1then the result is< - If the left string is empty then the result is
< - 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.
Python 2, 84 79 76 bytes
lambda*l:cmp(*map(lambda v:map(int,v.split('.')+[0]*len(`l`))[:len(`l`)],l))
Outputs -1,0,1 for <,=,>
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])))
Outputs -1,0,1 for <,=,>, respectively.