Interview question
Compare semantic version strings
Implements comparison for simple semantic version strings with missing patch parts treated as zero.
TL;DR
Implements comparison for simple semantic version strings with missing patch parts treated as zero.
Parsing, numeric comparison, validation, missing parts, and release-version reasoning.
Practice the problem like a real interview: restate, reason, implement, and test.
Implement CompareVersions(string left, string right) returning -1, 0, or 1. Support numeric major.minor.patch, allow missing minor or patch as zero, and ignore prerelease labels for this exercise.
Next in Coding Practice: Invoice Total
CompareVersions("1.2.10", "1.3.0") returns -1.
CompareVersions("2.0", "2.0.0") returns 0.
ArgumentException.I would remove any prerelease or build metadata suffix, parse the remaining version into exactly three integers, and fill missing parts with zero. Then I compare major, minor, and patch in order. I would not compare version strings lexicographically, because 1.10.0 would incorrectly sort before 1.2.0. I would also clarify that real semantic versioning has more detailed prerelease ordering rules; this exercise intentionally ignores them.
public static int CompareVersions(string left, string right)
{
var leftParts = ParseVersion(left);
var rightParts = ParseVersion(right);
for (var i = 0; i < 3; i++)
{
var comparison = leftParts[i].CompareTo(rightParts[i]);
if (comparison != 0)
{
return Math.Sign(comparison);
}
}
return 0;
}
private static int[] ParseVersion(string version)
{
if (string.IsNullOrWhiteSpace(version))
{
throw new ArgumentException("Version is required.", nameof(version));
}
var coreVersion = version.Split('+', 2)[0].Split('-', 2)[0];
var rawParts = coreVersion.Split('.');
if (rawParts.Length > 3)
{
throw new ArgumentException("Version cannot have more than three parts.", nameof(version));
}
var parts = new[] { 0, 0, 0 };
for (var i = 0; i < rawParts.Length; i++)
{
if (!int.TryParse(rawParts[i], out var value) || value < 0)
{
throw new ArgumentException($"Invalid version part '{rawParts[i]}'.", nameof(version));
}
parts[i] = value;
}
return parts;
}
Parsing and comparison are O(1) because the method allows at most three numeric parts. Space is also O(1).
1.10.0 versus 1.2.0.1.2.3-alpha compared as 1.2.3 for this exercise.1.10 versus 1.2.2, 2.0, and 2.0.0 to verify missing parts.1.0.0-beta to confirm the suffix is ignored here.Spot a weak answer, missing edge case, or clearer explanation? Send it in.