Мне нравится версия от @ mar10 , хотя, с моей точки зрения, есть вероятность неправильного использования (кажется, что это не тот случай, если версии совместимы с документом семантического контроля версий , но может быть и в случае использования некоторого «номера сборки»). ):
versionCompare( '1.09', '1.1'); // returns 1, which is wrong: 1.09 < 1.1
versionCompare('1.702', '1.8'); // returns 1, which is wrong: 1.702 < 1.8
Проблема здесь в том, что под номера номера версии в некоторых случаях записываются с вырезанными конечными нулями (по крайней мере, как я недавно это видел при использовании другого программного обеспечения), что похоже на рациональную часть числа, поэтому:
5.17.2054 > 5.17.2
5.17.2 == 5.17.20 == 5.17.200 == ...
5.17.2054 > 5.17.20
5.17.2054 > 5.17.200
5.17.2054 > 5.17.2000
5.17.2054 > 5.17.20000
5.17.2054 < 5.17.20001
5.17.2054 < 5.17.3
5.17.2054 < 5.17.30
Однако первый (или оба, первый и второй) под номера версии всегда обрабатываются как целочисленное значение, которому они фактически равны.
Если вы используете этот тип управления версиями, вы можете изменить всего несколько строк в примере:
// replace this:
p1 = parseInt(v1parts[i], 10);
p2 = parseInt(v2parts[i], 10);
// with this:
p1 = i/* > 0 */ ? parseFloat('0.' + v1parts[i], 10) : parseInt(v1parts[i], 10);
p2 = i/* > 0 */ ? parseFloat('0.' + v2parts[i], 10) : parseInt(v2parts[i], 10);
Таким образом, каждый под-номер, кроме первого, будет сравниваться как число с плавающей запятой, а значит 09
и 1
станет 0.09
и, 0.1
соответственно , и будет соответствующим образом сопоставлен. 2054
и 3
станет 0.2054
и 0.3
.
Полная версия тогда (кредиты @ mar10 ):
/** Compare two dotted version strings (like '10.2.3').
* @returns {Integer} 0: v1 == v2, -1: v1 < v2, 1: v1 > v2
*/
function versionCompare(v1, v2) {
var v1parts = ("" + v1).split("."),
v2parts = ("" + v2).split("."),
minLength = Math.min(v1parts.length, v2parts.length),
p1, p2, i;
// Compare tuple pair-by-pair.
for(i = 0; i < minLength; i++) {
// Convert to integer if possible, because "8" > "10".
p1 = i/* > 0 */ ? parseFloat('0.' + v1parts[i], 10) : parseInt(v1parts[i], 10);;
p2 = i/* > 0 */ ? parseFloat('0.' + v2parts[i], 10) : parseInt(v2parts[i], 10);
if (isNaN(p1)){ p1 = v1parts[i]; }
if (isNaN(p2)){ p2 = v2parts[i]; }
if (p1 == p2) {
continue;
}else if (p1 > p2) {
return 1;
}else if (p1 < p2) {
return -1;
}
// one operand is NaN
return NaN;
}
// The longer tuple is always considered 'greater'
if (v1parts.length === v2parts.length) {
return 0;
}
return (v1parts.length < v2parts.length) ? -1 : 1;
}
PS Медленнее, но также можно подумать о повторном использовании той же функции сравнения, работающей на том факте, что строка на самом деле является массивом символов:
function cmp_ver(arr1, arr2) {
// fill the tail of the array with smaller length with zeroes, to make both array have the same length
while (min_arr.length < max_arr.length) {
min_arr[min_arr.lentgh] = '0';
}
// compare every element in arr1 with corresponding element from arr2,
// but pass them into the same function, so string '2054' will act as
// ['2','0','5','4'] and string '19', in this case, will become ['1', '9', '0', '0']
for (i: 0 -> max_length) {
var res = cmp_ver(arr1[i], arr2[i]);
if (res !== 0) return res;
}
}