Continuing from my comment, I would convert all fields of the version to [float]
. Before conversion, if a version field starts with zero, I would interpret it as a fraction of 1
by inserting a .
after the first 0
.
So 14.03.0.0
becomes the sequence of floating point numbers 14.0
, 0.3
, 0.0
, 0.0
.
Similarly, 14.003.01.0
becomes the sequence of floating point numbers 14.0
, 0.03
, 0.1
, 0.0
.
Simplest solution
$thisversion = "14.03.0.0"
$nextversion = "14.1.0.56686"
write-host $thisversion
write-host $nextversion
# Transform the version strings into arrays of floating point numbers,
# which are fractions of 1 if a field starts with '0'.
[float[]] $thisversionArray = $thisversion.Split('.') -replace '^0', '0.'
[float[]] $nextversionArray = $nextversion.Split('.') -replace '^0', '0.'
if( [Collections.StructuralComparisons]::StructuralComparer.Compare( $thisversionArray, $nextversionArray ) -lt 0 ) {
write-host "$thisversion is less then $nextversion"
}
More elaborate test:
(
( '14.03.0.0' , '14.1.0.56686' ),
( '14.003.0.0', '14.03.0.0' ),
( '14.03.0.0' , '14.02.0.0' ),
( '14.03.0.0' , '14.03.0.0' ),
( '10.0.0.0' , '2.0.0.0' ),
( '10.0' , '2.0.0' )
).ForEach{
[float[]] $v1 = $_[0].Split('.') -replace '^0', '0.'
[float[]] $v2 = $_[1].Split('.') -replace '^0', '0.'
[Array]::Resize( [ref] $v1, 4 )
[Array]::Resize( [ref] $v2, 4 )
[PSCustomObject]@{
Version1 = $_[0]
Version2 = $_[1]
CompareResult = [Collections.StructuralComparisons]::StructuralComparer.Compare( $v1, $v2 )
}
}
Output:
Version1 Version2 CompareResult
-------- -------- -------------
14.03.0.0 14.1.0.56686 -1
14.003.0.0 14.03.0.0 -1
14.03.0.0 14.02.0.0 1
14.03.0.0 14.03.0.0 0
10.0.0.0 2.0.0.0 1
10.0 2.0.0 1
Extended solution
You may want to encapsulate version numbers with [float]
fields in a dedicated class, similar to [Version]
, to be able to use PowerShell's standard comparison operators like -lt
, -eq
and -gt
.
The following class FloatVersion
parses version numbers that may contain leading zeros and implements the IComparable
interface to support the standard comparison operators.
The floating point numbers that make up the version are stored as [Tuple[float,float,float,float]]
, which already provides lexicographical comparison.
class FloatVersion : System.IComparable
{
[Tuple[float,float,float,float]] $Fields
# Default constructor
FloatVersion() { $this.Fields = [Tuple]::Create( [float]0.0, [float]0.0, [float]0.0, [float]0.0 ) }
# Convert from string
FloatVersion( [string] $version ) {
# Split version into array of floats. If field starts with '0', it is interpreted as a fraction of 1.
[float[]] $v = $version.Split('.') -replace '^0', '0.'
# Ensure array has 4 elements, so we don't get an exception in strict mode.
[Array]::Resize( [ref] $v, 4 )
# Convert array to tuple
$this.Fields = [Tuple]::Create( $v[0], $v[1], $v[2], $v[3] )
}
# Implements the IComparable interface
[int] CompareTo( [object] $obj ) {
if( $null -eq $obj ) { return 1 }
$otherVersion = $obj -as [FloatVersion]
if( $null -ne $otherVersion ) {
return ([IComparable]$this.Fields).CompareTo( $otherVersion.Fields )
}
throw [ArgumentException]::new('Object is not a FloatVersion')
}
# Cheap conversion to string using the tuple's ToString() method.
# TODO: A more elaborate implementation that reproduces the input string.
[string] ToString() { return $this.Fields.ToString() }
}
Usage example:
[FloatVersion]'14.03.0.0' -lt [FloatVersion]'14.1.0.56686'
# "True"
More elaborate test:
(
( '14.03.0.0' , '14.1.0.56686' ),
( '14.003.0.0', '14.03.0.0' ),
( '14.03.0.0' , '14.02.0.0' ),
( '14.03.0.0' , '14.03.0.0' ),
( '10.0.0.0' , '2.0.0.0' ),
( '10.0' , '2.0.0' )
).ForEach{
[PSCustomObject]@{
Version1 = $_[0]
Version2 = $_[1]
CompareResult = ([FloatVersion] $_[0]).CompareTo( [FloatVersion] $_[1] )
}
}
Output:
Version1 Version2 CompareResult
-------- -------- -------------
14.03.0.0 14.1.0.56686 -1
14.003.0.0 14.03.0.0 -1
14.03.0.0 14.02.0.0 1
14.03.0.0 14.03.0.0 0
10.0.0.0 2.0.0.0 1
10.0 2.0.0 1