以下是软件版本号:
"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"
这样可以更清楚地看到背后的想法。
但是,我怎样才能把它转换成计算机程序呢?
我不喜欢任何一个解决方案,所以我根据自己的编码偏好重新编写了它。请注意,最后四个检查结果与接受的答案略有不同。对我有用。
function v_check(version_a, version_b) {
// compares version_a as it relates to version_b
// a = b => "same"
// a > b => "larger"
// a < b => "smaller"
// NaN => "invalid"
const arr_a = version_a.split('.');
const arr_b = version_b.split('.');
let result = "same"; // initialize to same // loop tries to disprove
// loop through a and check each number against the same position in b
for (let i = 0; i < arr_a.length; i++) {
let a = arr_a[i];
let b = arr_b[i];
// same up to this point so if a is not there, a is smaller
if (typeof a === 'undefined') {
result = "smaller";
break;
// same up to this point so if b is not there, a is larger
} else if (typeof b === 'undefined') {
result = "larger";
break;
// otherwise, compare the two numbers
} else {
// non-positive numbers are invalid
if (a >= 0 && b >= 0) {
if (a < b) {
result = "smaller";
break;
}
else if (a > b) {
result = "larger";
break;
}
} else {
result = "invalid";
break;
}
}
}
// account for the case where the loop ended but there was still a position in b to evaluate
if (result == "same" && arr_b.length > arr_a.length) result = "smaller";
return result;
}
console.log(v_check("1.7.1", "1.7.10")); // smaller
console.log(v_check("1.6.1", "1.7.10")); // smaller
console.log(v_check("1.6.20", "1.7.10")); // smaller
console.log(v_check("1.7.1", "1.7.10")); // smaller
console.log(v_check("1.7", "1.7.0")); // smaller
console.log(v_check("1.7", "1.8.0")); // smaller
console.log(v_check("1.7.10", "1.7.1")); // larger
console.log(v_check("1.7.10", "1.6.1")); // larger
console.log(v_check("1.7.10", "1.6.20")); // larger
console.log(v_check("1.7.0", "1.7")); // larger
console.log(v_check("1.8.0", "1.7")); // larger
console.log(v_check("1.7.10", "1.7.10")); // same
console.log(v_check("1.7", "1.7")); // same
console.log(v_check("1.7", "1..7")); // larger
console.log(v_check("1.7", "Bad")); // invalid
console.log(v_check("1..7", "1.7")); // smaller
console.log(v_check("Bad", "1.7")); // invalid
进行这种比较的基本思想是使用Array。拆分以从输入字符串中获得部件数组,然后比较两个数组中的部件对;如果部分不相等,我们就知道哪个版本更小。
这里有一些重要的细节需要记住:
每对零件应该如何比较?这个问题想要从数字上进行比较,但是如果我们有不只是由数字组成的版本字符串(例如。“1.0”)?
如果一个版本字符串的部分比另一个多,会发生什么?很可能“1.0”应该被认为小于“1.0.1”,但是“1.0.0”呢?
下面是你可以直接使用的实现代码(要点和文档):
function versionCompare(v1, v2, options) {
var lexicographical = options && options.lexicographical,
zeroExtend = options && options.zeroExtend,
v1parts = v1.split('.'),
v2parts = v2.split('.');
function isValidPart(x) {
return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
}
if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
return NaN;
}
if (zeroExtend) {
while (v1parts.length < v2parts.length) v1parts.push("0");
while (v2parts.length < v1parts.length) v2parts.push("0");
}
if (!lexicographical) {
v1parts = v1parts.map(Number);
v2parts = v2parts.map(Number);
}
for (var i = 0; i < v1parts.length; ++i) {
if (v2parts.length == i) {
return 1;
}
if (v1parts[i] == v2parts[i]) {
continue;
}
else if (v1parts[i] > v2parts[i]) {
return 1;
}
else {
return -1;
}
}
if (v1parts.length != v2parts.length) {
return -1;
}
return 0;
}
这个版本自然地比较各个部分,不接受字符后缀,并认为“1.7”比“1.7.0”小。比较模式可以更改为字典式,短版本字符串可以使用可选的第三个参数自动填充零。
这里有一个运行“单元测试”的JSFiddle;这是一个稍微扩展的版本的ripper234的工作(谢谢)。
重要提示:此代码使用Array。map和Array。这意味着它将不会在9之前的IE版本中运行。如果你需要支持这些方法,你就必须为缺失的方法提供填充。
我已经创建了这个解决方案,我希望你觉得它有用:
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)
这个非常小,但非常快的比较函数接受每个段的任何长度和任何数字大小的版本号。
返回值:
-如果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"]
虽然这个问题已经有很多答案,但每个人都在推广自己的解决方案,而我们有一个完整的生态系统(战斗)测试库。
在NPM, GitHub, X上快速搜索会给我们一些可爱的lib,我想要运行一些:
Semver-compare是一个很棒的轻量级库(约230字节),如果您想按版本号排序,它尤其有用,因为库的公开方法会适当地返回-1、0或1。
库的核心:
module.exports = function cmp (a, b) {
var pa = a.split('.');
var pb = b.split('.');
for (var i = 0; i < 3; i++) {
var na = Number(pa[i]);
var nb = Number(pb[i]);
if (na > nb) return 1;
if (nb > na) return -1;
if (!isNaN(na) && isNaN(nb)) return 1;
if (isNaN(na) && !isNaN(nb)) return -1;
}
return 0;
};
compare-semver的大小相当大(大约4.4 kB gzip),但是允许进行一些很好的惟一比较,比如查找一堆版本的最小/最大值,或者查找所提供的版本是惟一的还是比版本集合中的其他任何版本都小。
compare-versions是另一个小库(大约630字节gzip),它很好地遵循规范,这意味着你可以比较带有alpha/beta标志甚至通配符的版本(比如minor/patch版本:1.0)。X或1.0)
重点是:如果你能通过你选择的包管理器找到合适的(单元)测试版本,那么并不总是需要从Stack Overflow复制粘贴代码。