Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I compare arbitrary version numbers?

Does anyone have code to compare two version numbers in JavaScript? I just want simple version comparisons (e.g. "1.0" vs "1.5.6"), and it should work with numbers or strings. It can ignore trailing beta identifiers like "1.5.6b4", but can otherwise expect the strings to be well-formed. The function should return a signed integer like a normal cmp function.

function cmpVersion(a, b)
  return less than one if a < b
  return 0 if a == b
  return greater than one if a > b

I have an answer, but will choose a better or more elegant solution over my own.

(I am using this to compare jQuery.browser.version numbers, but the answer will be more widely applicable)

like image 274
theazureshadow Avatar asked Oct 10 '11 18:10

theazureshadow


People also ask

How do I compare two versions of numbers?

To compare version numbers, compare their revisions in left-to-right order. Revisions are compared using their integer value ignoring any leading zeros. This means that revisions 1 and 001 are considered equal. If a version number does not specify a revision at an index, then treat the revision as 0 .

How do I compare two versions of strings?

compareVersions(String v1, String v2) compares two version strings. It returns 0 if the versions are equal, 1 if version v1 is before version v2, -1 if version v1 is after version v2, -2 if version format is invalid.

How can I compare two Javascript versions?

The basic idea to make this comparison would be to use Array. split to get arrays of parts from the input strings and then compare pairs of parts from the two arrays; if the parts are not equal we know which version is smaller.


2 Answers

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.

like image 53
Gras Double Avatar answered Sep 28 '22 01:09

Gras Double


If you want to be fully correct, take a look at the discussion on PEP386, especially the heading “the new versioning algorithm”.

Otherwise it seems like your answer is pretty good.

like image 27
David Wolever Avatar answered Sep 28 '22 00:09

David Wolever