How can I compare arbitrary version numbers?

后端 未结 8 1735
半阙折子戏
半阙折子戏 2020-12-06 02:15

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 shou

8条回答
  •  眼角桃花
    2020-12-06 03:02

    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.

提交回复
热议问题