I have crafted the following function, which supports trailing letters, leading zeroes… (see examples below):
function cmpVersions(a, b) {
var partsA = a.split('.');
var partsB = b.split('.');
var nbParts = Math.max(partsA.length, partsB.length);
for (var i = 0; i < nbParts; ++i) {
if (partsA[i] === undefined) {
partsA[i] = '0';
}
if (partsB[i] === undefined) {
partsB[i] = '0';
}
// edit: added this part
// - fixes the important case "1.2 / 1.10"
// - but breaks the not-so-important case "1.02 / 1.1"
var intA = parseInt(partsA[i], 10);
var intB = parseInt(partsB[i], 10);
if (!isNaN(intA) && !isNaN(intB)) {
if (intA > intB) {
return 1;
} else if (intA < intB) {
return -1;
}
}
var compare = partsA[i].localeCompare(partsB[i]);
if (compare !== 0) {
return compare;
}
}
return 0;
}
So, a few examples:
// trailing letters
cmpVersion('1.0a', '1.0b'); // -1
// leading zeroes
cmpVersion('1.01', '1.1'); // -1
// "zero" parts
cmpVersion('1', '1.0'); // 0
If you don't need to support leading zeroes, here is a simpler alternative:
function cmpVersions(a, b) {
function padParts(version) {
return version
.split('.')
.map(function (part) {
return '00000000'.substr(0, 8 - part.length) + part;
})
.join('.');
}
a = padParts(a);
b = padParts(b);
return a.localeCompare(b);
}
Quick update: I noticed afterwards that the first function sorts "1.2" before "1.10", which is blatantly wrong. Also, the "significant leading zeroes" are tricky and ambiguous (both to interpret and to implement), and Semantic Versioning explicitly avoids them. Therefore, I think the second function should always be preferred.
Update 2: But the second function sorts "1.2a" before "1.1"... I think there is just no "one fits all" function... Pick the "more appropriate" function according to your use case, or better, rather sort by date if you can.
Update 3: Modified the first function to handle correctly the important case "1.2 / 1.10". As a side effect, it breaks the not-so-important case "1.02 / 1.1", and apparently it's now the only caveat (maybe it could be fixed, but I'm not sure it's worth it). Therefore, I'm now recommending the fixed, first function.