以下是软件版本号:

"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"

我怎么比较呢?

假设正确的顺序是:

"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"

想法很简单… 读第一个数字,然后,第二个,第三个… 但是我不能将版本号转换为浮点数… 你也可以像这样看到版本号:

"1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1.0"

这样可以更清楚地看到背后的想法。 但是,我怎样才能把它转换成计算机程序呢?


当前回答

2020年(大多数时候)正确的JavaScript答案

Nina Scholz在2020年3月和Sid Vishnoi在2020年4月都给出了现代的答案:

var versions = ["2.0.1", "2.0", "1.0", "1.0.1", "2.0.0.1"];

versions.sort((a, b) => 
   a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })
);

console.log(versions);

localCompare已经存在一段时间了

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator

但是1.0a和1.0.1呢

localCompare不能解决这个问题,仍然返回1.0.1,1.0a

迈克尔·迪尔(Michael Deal)在他的(略长且复杂的)解决方案中已经在2013年解决了这个问题

他将数字转换为另一种进位,以便更好地排序

他的回答让我思考……

666 -不要用数字思考- 999

排序是基于ASCII值的字母数字排序,所以让我们(ab)使用ASCII作为“基”

我的解决方案是将1.0.2.1到b.a.c.b转换为bacb,然后排序

这解决了1.1 vs. 1.0.0.0.1: bb vs. baaab

立即用baa和bab符号解决了1.0a和1.0.1排序问题

转换是通过:

    const str = s => s.match(/(\d+)|[a-z]/g)
                      .map(c => c == ~~c ? String.fromCharCode(97 + c) : c);

=计算ASCII值0…999数字,否则连字母

1.0 > > >(“0”,“1”” " ] >>> [ " b”、“”、“”)

为了便于比较,没有必要使用.join("")将其连接到一个字符串。

Oneliner

const sortVersions=(x,v=s=>s.match(/(\d+)|[a-z]/g)
                            .map(c=>c==~~c?String.fromCharCode(97+c):c))
                    =>x.sort((a,b)=>v(b)<v(a)?1:-1)

测试代码片段:

function log(label,val){ document.body.append(label,String(val).replace(/,/g," - "),document.createElement("BR")); } let v = ["1.90.1", "1.9.1", "1.89", "1.090", "1.2", "1.0a", "1.0.1", "1.10", "1.0.0a"]; log('not sorted input :',v); v.sort((a, b) => a.localeCompare(b,undefined,{numeric:true,sensitivity:'base' })); log(' locale Compare :', v); // 1.0a AFTER 1.0.1 const str = s => s.match(/(\d+)|[a-z]/g) .map(c => c == ~~c ? String.fromCharCode(97 + c) : c); const versionCompare = (a, b) => { a = str(a); b = str(b); return b < a ? 1 : a == b ? 0 : -1; } v.sort(versionCompare); log('versionCompare:', v);

注意1.090是如何在两个结果中排序的。

我的代码不会解决一个答案中提到的001.012.001符号,但是localeCompare正确地解决了这部分挑战。

你可以结合这两种方法:

当涉及字母时,使用.localCompare或versionCompare进行排序

最终的JavaScript解决方案

const sortVersions = ( x, V = s => s.match(/[a-z]|\d+/g)。Map (c => c==~~c ?String.fromCharCode(97 + c): c) => x.sort((a, b) => (a + b).match(/[a-z]/) ? V (b) < V (a) ?1: -1 : a.localeCompare(b, 0, {numeric: true})) 让v =[" 1.90.1”、“1.090”、“1.0”、“1.0.1”,“1.0.0a”,“1.0.0b”、“1.0.0.1”); console.log (sortVersions (v));

其他回答

基于Idan的精彩回答,下面的函数semverCompare通过了语义版本2.0.0的大多数情况。了解更多要点。

function semverCompare(a, b) {
    if (a.startsWith(b + "-")) return -1
    if (b.startsWith(a + "-")) return  1
    return a.localeCompare(b, undefined, { numeric: true, sensitivity: "case", caseFirst: "upper" })
}

它返回:

-1: a < b 0: a == b 1: a > b

2017答:

v1 = '20.0.12'; 
v2 = '3.123.12';

compareVersions(v1,v2) 
// return positive: v1 > v2, zero:v1 == v2, negative: v1 < v2 
function compareVersions(v1, v2) {
        v1= v1.split('.')
        v2= v2.split('.')
        var len = Math.max(v1.length,v2.length)
        /*default is true*/
        for( let i=0; i < len; i++)
            v1 = Number(v1[i] || 0);
            v2 = Number(v2[i] || 0);
            if (v1 !== v2) return v1 - v2 ;
            i++;
        }
        return 0;
    }

最简单的现代浏览器代码:

 function compareVersion2(ver1, ver2) {
      ver1 = ver1.split('.').map( s => s.padStart(10) ).join('.');
      ver2 = ver2.split('.').map( s => s.padStart(10) ).join('.');
      return ver1 <= ver2;
 }

这里的想法是比较数字,但以字符串的形式。为了使比较工作,两个字符串必须在相同的长度。所以:

"123" > "99"变成"123" > "099" 填充短数字“修复”比较

这里我用0填充每个部分,长度为10。然后使用简单的字符串比较来得到答案

例子:

var ver1 = '0.2.10', ver2=`0.10.2`
//become 
ver1 = '0000000000.0000000002.0000000010'
ver2 = '0000000000.0000000010.0000000002'
// then it easy to see that
ver1 <= ver2 // true

下面是另一个简短的版本,适用于任何数量的子版本,填充零和偶数字母(1.0.0b3)

const compareVer = ((prep, repl) =>
{
  prep = t => ("" + t)
      //treat non-numerical characters as lower version
      //replacing them with a negative number based on charcode of first character
    .replace(/[^0-9\.]+/g, c => "." + (c.replace(/[\W_]+/, "").toLowerCase().charCodeAt(0) - 65536) + ".")
      //remove trailing "." and "0" if followed by non-numerical characters (1.0.0b);
    .replace(/(?:\.0+)*(\.-[0-9]+)(\.[0-9]+)?\.*$/g, "$1$2")
    .split('.');

  return (a, b, c, i, r) =>
  {
    a = prep(a);
    b = prep(b);
    for (i = 0, r = 0, c = Math.max(a.length, b.length); !r && i++ < c;)
    {
      r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]);
    }
    return r;
  }
})();

函数返回:

如果a = b则为0

1如果a > b

-1如果a < b

1.0         = 1.0.0.0.0.0
1.0         < 1.0.1
1.0b1       < 1.0
1.0b        = 1.0b
1.1         > 1.0.1b
1.1alpha    < 1.1beta
1.1rc1      > 1.1beta
1.1rc1      < 1.1rc2
1.1.0a1     < 1.1a2
1.1.0a10    > 1.1.0a1
1.1.0alpha  = 1.1a
1.1.0alpha2 < 1.1b1
1.0001      > 1.00000.1.0.0.0.01

/*use strict*/ const compareVer = ((prep, repl) => { prep = t => ("" + t) //treat non-numerical characters as lower version //replacing them with a negative number based on charcode of first character .replace(/[^0-9\.]+/g, c => "." + (c.replace(/[\W_]+/, "").toLowerCase().charCodeAt(0) - 65536) + ".") //remove trailing "." and "0" if followed by non-numerical characters (1.0.0b); .replace(/(?:\.0+)*(\.-[0-9]+)(\.[0-9]+)?\.*$/g, "$1$2") .split('.'); return (a, b, c, i, r) => { a = prep(a); b = prep(b); for (i = 0, r = 0, c = Math.max(a.length, b.length); !r && i++ < c;) { r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]); } return r; } })(); //examples let list = [ ["1.0", "1.0.0.0.0.0"], ["1.0", "1.0.1"], ["1.0b1", "1.0"], ["1.0b", "1.0b"], ["1.1", "1.0.1b"], ["1.1alpha", "1.1beta"], ["1.1rc1", "1.1beta"], ["1.1rc1", "1.1rc2"], ["1.1.0a1", "1.1a2"], ["1.1.0a10", "1.1.0a1"], ["1.1.0alpha", "1.1a"], ["1.1.0alpha2", "1.1b1"], ["1.0001", "1.00000.1.0.0.0.01"] ] for(let i = 0; i < list.length; i++) { console.log( list[i][0] + " " + "<=>"[compareVer(list[i][0], list[i][1]) + 1] + " " + list[i][1] ); }

https://jsfiddle.net/vanowm/p7uvtbor/

这个非常小,但非常快的比较函数接受每个段的任何长度和任何数字大小的版本号。

返回值: -如果a < b,则数字< 0 -如果是> b,则为> 0 如果a = b - 0

所以你可以使用它作为array。sort()的比较函数;

编辑:修正了版本剥离尾随零识别“1”和“1.0.0”相等的错误

function cmpVersions (a, b) { var i, diff; var regExStrip0 = /(\.0+)+$/; var segmentsA = a.replace(regExStrip0, '').split('.'); var segmentsB = b.replace(regExStrip0, '').split('.'); var l = Math.min(segmentsA.length, segmentsB.length); for (i = 0; i < l; i++) { diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10); if (diff) { return diff; } } return segmentsA.length - segmentsB.length; } // TEST console.log( ['2.5.10.4159', '1.0.0', '0.5', '0.4.1', '1', '1.1', '0.0.0', '2.5.0', '2', '0.0', '2.5.10', '10.5', '1.25.4', '1.2.15'].sort(cmpVersions)); // Result: // ["0.0.0", "0.0", "0.4.1", "0.5", "1.0.0", "1", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"]

我已经创建了这个解决方案,我希望你觉得它有用:

https://runkit.com/ecancino/5f3c6c59593d23001485992e


const quantify = max => (n, i) => n * (+max.slice(0, max.length - i))

const add = (a, b) => a + b

const calc = s => s.
    split('.').
    map(quantify('1000000')).
    reduce(add, 0)

const sortVersions = unsortedVersions => unsortedVersions
    .map(version => ({ version, order: calc(version) }))
    .sort((a, b) => a.order - b.order)
    .reverse()
    .map(o => o.version)