Как я могу сравнить произвольные номера версий? - PullRequest
9 голосов
/ 10 октября 2011

У кого-нибудь есть код для сравнения двух номеров версий в JavaScript? Я просто хочу простого сравнения версий (например, "1.0" против "1.5.6"), и оно должно работать с числами или строками. Он может игнорировать конечные бета-идентификаторы, такие как "1.5.6b4", но может ожидать, что строки будут правильно сформированы. Функция должна возвращать целое число со знаком, как обычная функция 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 ]

14 голосов
/ 10 октября 2011
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)
  • игнорирует конечныйalpha, b, pre4 и т. Д.
4 голосов
/ 10 октября 2011

Если вы хотите быть полностью корректным, взгляните на обсуждение PEP386 , особенно заголовок «новый алгоритм управления версиями».

В противном случае кажется, что ваш ответ довольно хороший.

2 голосов
/ 15 декабря 2013

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 будет.
  • <=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.2 и 2.0.0-beta не удовлетворят.
  • ^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"

Диапазоны могут быть объединены либо пробелом (что подразумевает "и"), либо || (что подразумевает "или").

0 голосов
/ 21 декабря 2017

Я создал следующую функцию, которая поддерживает конечные буквы, ведущие нули ... (см. Примеры ниже):

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


Быстрое обновление: Впоследствии я заметил, что первая функция сортирует «1.2»до "1.10", что явно неправильно.Кроме того, «значимые начальные нули» хитры и неоднозначны (как для интерпретации, так и для реализации), и семантическое управление версиями явно избегает их.Поэтому я думаю, что вторая функция всегда должна быть предпочтительнее.

Обновление 2: Но вторая функция сортирует «1.2a» перед «1.1» ... Я думаю, что просто нет »одна подходит для всех "функций ... Выберите" более подходящую "функцию в соответствии с вашим вариантом использования, или, лучше, по возможности сортируйте по дате.

Обновление 3: Изменено первоефункция для правильной обработки важного случая "1.2 / 1.10".Как побочный эффект, это нарушает не столь важный случай "1.02 / 1.1", и, очевидно, это теперь единственное предупреждение (возможно, это можно исправить, но я не уверен, что оно того стоит).Поэтому сейчас я рекомендую фиксированную первую функцию.

0 голосов
/ 04 июля 2017

Я реорганизовал мой, чтобы быть настолько кратким, насколько я могу сделать это.У него нет проверки на конечные нули, но он будет работать для номеров сборки любой длины (например, major, major.minor, major.minor.build).

var cmpVersion = function(a, b) {
  let arrA = Array.from(a.split('.'), i => +i);
  let arrB = Array.from(b.split('.'), i => +i);

  for (let i = 0; i < (arrA.length >= arrB.length ? arrA.length : arrB.length); i++) {
    if (arrA[i] && !arrB[i] || arrA[i] > arrB[i]) return 'less than one';
    else if (!arrA[i] && arrB[i] || arrA[i] < arrB[i]) return 'greater than one';
  }
  return 0;
}

По сути, сначала я создал новый массивиз каждой строки версии, чтобы я мог сравнить каждую цифру в отдельности.Затем в цикле for я выбираю длину самой длинной строки версии (или длину первой строки версии, если они имеют одинаковую длину).

Оператор if проверяет, есть ли цифрав a, но не в b, или если цифра a больше, чем цифра b для соответствующего заполнителя, и в этом случае она будет возвращать «меньше единицы».

Кроме того, оператор else проверяетпосмотрите, есть ли цифра в b, но не в a, или если цифра b больше, чем цифра a для соответствующего значения места, и в этом случае она вернет «больше единицы».

Последний оператор return 0 является универсальным, что наша функция получит, если строки версии равны.

0 голосов
/ 23 декабря 2015

Эта функция возвращает истину, если версия больше или равна минимальной версии.Предполагается, что 1.0 больше 1, когда версии являются строками.Когда они числа, они говорят, что они одинаковы.Если вы хотите, чтобы оба типа возвращали одно и то же, вам нужно преобразовать числа в строки, что также легко.или вы можете изменить строковое условие, чтобы проверить, имеют ли более длинный номер версии все завершающие нули, такие как 1.1 против 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);
}
0 голосов
/ 10 октября 2011
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));
}
0 голосов
/ 10 октября 2011

Если вас не волнует .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") );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...