如何比较任意版本号?

时间:2011-10-10 18:37:12

标签: javascript comparison

有没有人有代码来比较JavaScript中的两个版本号?我只想进行简单的版本比较(例如"1.0" vs "1.5.6"),它应该与数字或字符串一起使用。它可以忽略"1.5.6b4"之类的尾随beta标识符,但可以预期字符串格式正确。该函数应该返回一个有符号整数,就像普通的cmp函数一样。

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

我有一个答案,但会选择一个比我更好或更优雅的解决方案。

(我用它来比较jQuery.browser.version个数字,但答案会更广泛适用)

8 个答案:

答案 0 :(得分:14)

function cmpVersion(a, b) {
    var i, cmp, len;
    a = (a + '').split('.');
    b = (b + '').split('.');
    len = Math.max(a.length, b.length);
    for( i = 0; i < len; i++ ) {
        if( a[i] === undefined ) {
            a[i] = '0';
        }
        if( b[i] === undefined ) {
            b[i] = '0';
        }
        cmp = parseInt(a[i], 10) - parseInt(b[i], 10);
        if( cmp !== 0 ) {
            return (cmp < 0 ? -1 : 1);
        }
    }
    return 0;
}

function gteVersion(a, b) {
    return cmpVersion(a, b) >= 0;
}
function ltVersion(a, b) {
    return cmpVersion(a, b) < 0;
}

此功能处理:

  • 数字或字符串作为输入
  • 尾随零(例如cmpVersion("1.0", 1)返回0
  • 忽略尾随alphabpre4

答案 1 :(得分:4)

如果您想完全正确,请查看PEP386上的讨论,尤其是标题“新版本控制算法”。

否则看起来你的答案非常好。

答案 2 :(得分:2)

npm使用一个很好的语法来比较版本,你可以在这里获得相同的模块:https://github.com/isaacs/node-semver

支持以下范围样式:

  • 1.2.3特定版本。什么都没做。请注意,仍然会忽略构建元数据,因此1.2.3+build2012将满足此范围。
  • >1.2.3大于特定版本。
  • <1.2.3少于特定版本。如果版本范围上没有预发布标记,则也不允许使用预发布版本,即使这些版本在技术上“小于”。
  • >=1.2.3大于或等于。请注意,预发布版本不等于其“普通”版本,因此1.2.3-beta不会满足此范围,但2.3.0-beta会。{/ li>
  • <=1.2.3小于或等于。在这种情况下,允许使用预发布版本,因此1.2.3-beta将满足。
  • 1.2.3 - 2.3.4:= >=1.2.3 <=2.3.4
  • ~1.2.3:= >=1.2.3-0 <1.3.0-0“合理接近1.2.3”。使用代字号运算符时,也支持预发布版本,但是下一个有效数字的预发布将不会令人满意,因此1.3.0-beta将不满足~1.2.3
  • ^1.2.3:= >=1.2.3-0 <2.0.0-0“与1.2.3兼容”。使用插入符号运算符时,将支持指定版本(包括预发行版)中的任何内容,但不包括下一个主要版本(或其预发行版)。 1.5.1将满足^1.2.3,而1.2.22.0.0-beta则不会。{/ li>
  • ^0.1.3:= >=0.1.3-0 <0.2.0-0“与0.1.3兼容”。 0.x.x版本是特殊的:第一个非零组件表示可能发生重大更改,这意味着插入符操作符匹配从指定版本开始的具有相同的第一个非零组件的任何版本。
  • ^0.0.2:= =0.0.2“只有版本0.0.2被视为兼容”
  • ~1.2:= >=1.2.0-0 <1.3.0-0“任何以1.2”开头的版本
  • ^1.2:= >=1.2.0-0 <2.0.0-0“任何与1.2”兼容的版本
  • 1.2.x:= >=1.2.0-0 <1.3.0-0“任何以1.2”开头的版本
  • ~1:= >=1.0.0-0 <2.0.0-0“任何以1”开头的版本
  • ^1:= >=1.0.0-0 <2.0.0-0“任何与1”
  • 兼容的版本
  • 1.x:= >=1.0.0-0 <2.0.0-0“任何以1”开头的版本

范围可以与空格(表示“和”)或a连接 ||(暗示“或”)。

答案 3 :(得分:0)

如果您不关心.5.6,请使用parseInt

var majorV = parseInt("1.5.6",10)

既然你说你关心次要版本:

function cmpVersion(v1, v2) {
    if(v1===v2) return 0;
    var a1 = v1.toString().split(".");
    var a2 = v2.toString().split(".");
    for( var i = 0; i < a1.length && i < a2.length; i++ ) {
        var diff = parseInt(a1[i],10) - parseInt(a2[i],10);
        if( diff>0 ) {
            return 1;
        }
        else if( diff<0 ) {
            return -1;
        }
    }
    diff = a1.length - a2.length;
    return (diff>0) ? 1 : (diff<0) ? -1 : 0;
}

console.log( cmpVersion( "1.0", "1.56") );
console.log( cmpVersion( "1.56", "1.56") );
console.log( cmpVersion( "1.65", "1.5.6") );
console.log( cmpVersion( "1.0", "1.5.6b3") );

答案 4 :(得分:0)

function compareVersion(a, b) {
    return compareVersionRecursive(a.split("."), b.split("."));
}

function compareVersionRecursive(a, b) {
    if (a.length == 0) {
        a = [0];
    }
    if (b.length == 0) {
        b = [0];
    }
    if (a[0] != b[0] || (a.length == 1 && b.length == 1)) {
        return a[0] - b[0];
    }
    return compareVersionRecursive(a.slice(1), b.slice(1));
}

答案 5 :(得分:0)

我已经重构了我,尽可能简洁。它没有检查尾随零,但适用于任何长度的内部版本号(例如major,major.minor,major.minor.build)。

filteredItems

基本上,我首先从每个版本字符串中创建一个新数组,以便我可以单独比较每个数字。然后在for循环中,我选择最长版本字符串的长度(如果它们的长度相等,则选择第一个版本字符串的长度)。

if语句检查a中是否有数字但b中是否有数字或a的数字是否大于相应占位符的b数字,在这种情况下它将返回小于一个&#39;

同样,else语句检查b中是否有数字但是不存在或者b的数字是否大于相应位值的a数字,在这种情况下它将返回&# 39;大于一个&#39;。

最后一个返回0语句是一个catch-all,如果版本字符串相同,我们的函数将会得到它。

答案 6 :(得分:0)

我制作了以下函数,它支持尾随字母,前导零...(参见下面的示例):

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;
}

所以,举几个例子:

// trailing letters
cmpVersion('1.0a', '1.0b'); // -1

// leading zeroes
cmpVersion('1.01', '1.1'); // -1

// "zero" parts
cmpVersion('1', '1.0'); // 0


如果您不需要支持前导零,这里有一个更简单的选择:

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);
}


快速更新:之后我注意到第一个功能排序&#34; 1.2&#34;之前&#34; 1.10&#34;,这是明显错误的。此外,&#34;重要的领先零&#34;是狡猾和模棱两可的(无论是解释还是实现),语义版本控制明确地避免了它们。因此,我认为第二个功能应该始终是首选。

更新2:但第二个功能排序&#34; 1.2a&#34;之前&#34; 1.1&#34; ...我认为没有&#34;一个适合所有人&#34;功能...选择&#34;更合适&#34;根据您的使用情况或更好的方式运行,如果可以,则按日期排序。

更新3:修改了第一个正确处理重要案例的函数&#34; 1.2 / 1.10&#34;。作为一个副作用,它打破了不那么重要的情况&#34; 1.02 / 1.1&#34;,显然它现在是唯一的警告(也许它可以修复,但我&#39; m不确定它是否值得)。因此,我现在推荐固定的第一个功能。

答案 7 :(得分:-1)

如果版本大于或等于最小版本,则此函数返回true。当版本为字符串时,假设1.0大于1。当它们是数字时,它表示它们是相同的。如果你想让两种类型都返回相同,那么你需要将数字转换为字符串,这也很容易。或者您可以修改字符串条件以检查较长版本号是否具有1.1 vs 1.1.0.0.0.0之类的所有尾随零。第二个是尾随零

 function doesMyVersionMeetMinimum(myVersion, minimumVersion) {

    if(typeof myVersion === 'number' && typeof minimumVersion === 'number') {
      return(myVersion >= minimumVersion);
    }

    var v1 = myVersion.split("."), v2 = minimumVersion.split("."), minLength;

    minLength= Math.min(v1.length, v2.length);

    for(i=0; i<minLength; i++) {
        if(Number(v1[i]) < Number(v2[i])) {
            return false;
        }
       else if(Number(v1[i]) < Number(v2[i])) {
            return true;
        }           

    }

    return (v1.length >= v2.length);
}