Как сравнить номер версии программного обеспечения с помощью js? (только номер) - PullRequest
142 голосов
/ 26 июля 2011

Вот номер версии программного обеспечения:

"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"

и это более ясно, чтобы понять, в чем идея ... Но как преобразовать его в компьютерную программу? У кого-нибудь есть идеи о том, как это отсортировать? Спасибо.

Ответы [ 38 ]

0 голосов
/ 13 августа 2015

Предварительная обработка версий перед сортировкой означает, что parseInt не вызывается без необходимости несколько раз. Используя Array # map, аналогичную предложению Майкла Дила, вот что я использую, чтобы найти новейшую версию стандартного трехчастного сервера:

var semvers = ["0.1.0", "1.0.0", "1.1.0", "1.0.5"];

var versions = semvers.map(function(semver) {
    return semver.split(".").map(function(part) {
        return parseInt(part);
    });
});

versions.sort(function(a, b) {
    if (a[0] < b[0]) return 1;
    else if (a[0] > b[0]) return -1;
    else if (a[1] < b[1]) return 1;
    else if (a[1] > b[1]) return -1;
    else if (a[2] < b[2]) return 1;
    else if (a[2] > b[2]) return -1;
    return 0;
});

var newest = versions[0].join(".");
console.log(newest); // "1.1.0"
0 голосов
/ 05 сентября 2013

Мне нравится версия от @ 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;
}

P.S. Это медленнее, но также можно подумать о повторном использовании той же функции сравнения, поскольку тот факт, что строка на самом деле является массивом символов:

 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;
     }
 }
0 голосов
/ 26 сентября 2018

это моё решение. он принял на leetcode. Я встретил проблему в моем интервью сегодня. Но я не решил это в то время. Я снова подумал об этом. Добавление нулей, чтобы сделать длину двух массивов равной. Тогда сравнение.

var compareVersion = function(version1, version2) {
    let arr1 = version1.split('.').map(Number);
    let arr2 = version2.split('.').map(Number);
    let diff = 0;
    if (arr1.length > arr2.length){
        diff = arr1.length - arr2.length;
        while (diff > 0){
            arr2.push(0);
            diff--;
        } 
    }
    else if (arr1.length < arr2.length){
        diff = arr2.length - arr1.length;
        while (diff > 0){
            arr1.push(0);
            diff--;
        }
    }
   
    let i = 0;
    while (i < arr1.length){
        if (arr1[i] > arr2[i]){
           return 1;
       } else if (arr1[i] < arr2[i]){
           return -1;
       }
        i++;
    }
    return 0;
    
};
0 голосов
/ 26 октября 2014

Это не совсем решение вопроса, но это очень похоже.

Эта функция сортировки предназначена для семантических версий , она обрабатывает разрешенную версию, поэтому она не работает с подстановочными знаками, такими как x или *.

Работает с версиями, в которых это регулярное выражение совпадает: /\d+\.\d+\.\d+.*$/. Это очень похоже на этот ответ за исключением того, что он работает также с версиями, такими как 1.2.3-dev. По сравнению с другим ответом: я удалил некоторые проверки, которые мне не нужны, но мое решение может быть объединено с другим.

semVerSort = function(v1, v2) {
  var v1Array = v1.split('.');
  var v2Array = v2.split('.');
  for (var i=0; i<v1Array.length; ++i) {
    var a = v1Array[i];
    var b = v2Array[i];
    var aInt = parseInt(a, 10);
    var bInt = parseInt(b, 10);
    if (aInt === bInt) {
      var aLex = a.substr((""+aInt).length);
      var bLex = b.substr((""+bInt).length);
      if (aLex === '' && bLex !== '') return 1;
      if (aLex !== '' && bLex === '') return -1;
      if (aLex !== '' && bLex !== '') return aLex > bLex ? 1 : -1;
      continue;
    } else if (aInt > bInt) {
      return 1;
    } else {
      return -1;
    }
  }
  return 0;
}

Объединенное решение таково:

function versionCompare(v1, v2, options) {
    var zeroExtend = options && options.zeroExtend,
        v1parts = v1.split('.'),
        v2parts = v2.split('.');

    if (zeroExtend) {
        while (v1parts.length < v2parts.length) v1parts.push("0");
        while (v2parts.length < v1parts.length) v2parts.push("0");
    }

    for (var i = 0; i < v1parts.length; ++i) {
        if (v2parts.length == i) {
            return 1;
        }
        var v1Int = parseInt(v1parts[i], 10);
        var v2Int = parseInt(v2parts[i], 10);
        if (v1Int == v2Int) {
            var v1Lex = v1parts[i].substr((""+v1Int).length);
            var v2Lex = v2parts[i].substr((""+v2Int).length);
            if (v1Lex === '' && v2Lex !== '') return 1;
            if (v1Lex !== '' && v2Lex === '') return -1;
            if (v1Lex !== '' && v2Lex !== '') return v1Lex > v2Lex ? 1 : -1;
            continue;
        }
        else if (v1Int > v2Int) {
            return 1;
        }
        else {
            return -1;
        }
    }

    if (v1parts.length != v2parts.length) {
        return -1;
    }

    return 0;
}
0 голосов
/ 28 ноября 2018

Хотя на этот вопрос уже есть лот ответов, каждый из них продвигает свое собственное решение, созданное на заднем дворе, в то время как для этого у нас есть целая экосистема (проверенных) библиотек.

Быстрый поиск по NPM , GitHub , X даст нам несколько прекрасных библиотек, и я бы хотел пробежаться по ним:

semver-compare - это очень легкая (~ 230B) библиотека, которая особенно полезна, если вы хотите сортировать по номерам версий, так как открытый метод библиотеки возвращает -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 КБ в сжатом виде), но допускает несколько приятных уникальных сравнений, таких как поиск минимума / максимума стека версий или выяснение, версия уникальна или меньше, чем что-либо еще в коллекции версий.

compare-versions - это еще одна небольшая библиотека (~ 630B в сжатом виде), которая хорошо соответствует спецификации, то есть вы можете сравнивать версии с флагами альфа / бета и даже с подстановочными знаками (как для минорных версий / версий патча: 1.0.x или 1.0.*)

Суть в том, что не всегда нужно копировать и вставлять код из StackOverflow, если вы можете найти достойные, (unit -) проверенные версии с помощью выбранного вами менеджера пакетов.

0 голосов
/ 16 ноября 2014

У меня была такая же проблема сравнения версий, но с версиями, возможно, содержащими что-либо (то есть: разделители, которые не были точками, расширения как rc1, rc2 ...).

Я использовал это, который в основном разделилверсия строится на числа и не числа и пытается сравнить соответственно с типом.

function versionCompare(a,b) {
  av = a.match(/([0-9]+|[^0-9]+)/g)
  bv = b.match(/([0-9]+|[^0-9]+)/g)
  for (;;) {
    ia = av.shift();
    ib = bv.shift();
    if ( (typeof ia === 'undefined') && (typeof ib === 'undefined') ) { return 0; }
    if (typeof ia === 'undefined') { ia = '' }
    if (typeof ib === 'undefined') { ib = '' }

    ian = parseInt(ia);
    ibn = parseInt(ib);
    if ( isNaN(ian) || isNaN(ibn) ) {
      // non-numeric comparison
      if (ia < ib) { return -1;}
      if (ia > ib) { return 1;}
    } else {
      if (ian < ibn) { return -1;}
      if (ian > ibn) { return 1;}
    }
  }
}

Здесь есть некоторые допущения для некоторых случаев, например: "1.01" === "1.1",или "1,8" <"1,71".Он не может управлять "1.0.0-rc.1" <"1.0.0", как указано в Semantic versionning 2.0.0 </p>

0 голосов
/ 01 апреля 2019

Функция вернет -1, если версия равна, 0, если первая версия самая последняя, ​​и 1, чтобы указать, что вторая версия самая последняя.

let v1 = '12.0.1.0'
let v2 = '12.0.1'

let temp1 = v1.split('.');
let temp2 = v2.split('.');

console.log(compareVersion(temp1, temp2))


function compareVersion(version1, version2) {
    let flag = false;
    var compareResult;
    let maxLength = Math.max(version1.length, version2.length); 
    let minLength = Math.min(version1.length, version2.length);

    for (let i = 0; i < maxLength; ++i ) {
        let result = version1[i] - version2[i];
        if (result > 0) {
            flag = true;
            compareResult = 0;
            break;
        }
        else if (result < 0) {
            flag = true;
            compareResult = 1;
            break;
        }

        if (i === minLength) {
            if (version1.length > version1.length) {
                compareResult = version1[version1.length-1] > 0 ? '0' : '-1'
            }  else  {
                compareResult = version1[version2.length-1] > 0 ? '1' : '-1'
            }
            break;
        }
    }
    if (flag === false) {
        compareResult = -1;
    }
    return compareResult;
}
0 голосов
/ 26 июля 2011

Вы можете перебрать каждый символ, разделенный точкой, и преобразовать его в целое число:

var parts = versionString.split('.');

for (var i = 0; i < parts.length; i++) {
  var value = parseInt(parts[i]);
  // do stuffs here.. perhaps build a numeric version variable?
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...