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

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

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

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

Ответ 1

Основная идея сделать это сравнение - использовать Array.split для получения массивов деталей из входных строк, а затем сравнить пары частей из двух массивов; если части не равны, мы знаем, какая версия меньше.

Есть несколько важных деталей, которые нужно помнить:

  • Как следует сравнивать части в каждой паре? Вопрос хочет сравнить численно, но что, если у нас есть строки версий, которые не состоят из простых цифр (например, "1.0a" )?
  • Что произойдет, если одна строка версии содержит больше частей, чем другая? Скорее всего, "1.0" следует считать меньше, чем "1.0.1", но как насчет "1.0.0"?

Вот код для реализации, который вы можете использовать напрямую (gist с документацией):

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 work (спасибо).

Важное примечание: Этот код использует Array.map и Array.every, а это означает, что он не будет запускаться в версиях IE раньше 9. Если вам нужно поддерживать их, вам придется предоставить полисы для отсутствующих методов.

Ответ 2

semver

Анализатор семантической версии, используемый npm.

$npm install semver

var semver = require('semver');

semver.diff('3.4.5', '4.3.7') //'major'
semver.diff('3.4.5', '3.3.7') //'minor'
semver.gte('3.4.8', '3.4.7') //true
semver.ltr('3.4.8', '3.4.7') //false

semver.valid('1.2.3') // '1.2.3'
semver.valid('a.b.c') // null
semver.clean(' =v1.2.3 ') // '1.2.3'
semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
semver.gt('1.2.3', '9.8.7') // false
semver.lt('1.2.3', '9.8.7') // true

var versions = [ '1.2.3', '3.4.5', '1.0.2' ]
var max = versions.sort(semver.rcompare)[0]
var min = versions.sort(semver.compare)[0]
var max = semver.maxSatisfying(versions, '*')

Связи с семантической версией:
https://www.npmjs.com/package/semver#prerelease-identifiers

Ответ 3

// Return 1 if a > b
// Return -1 if a < b
// Return 0 if a == b
function compare(a, b) {
    if (a === b) {
       return 0;
    }

    var a_components = a.split(".");
    var b_components = b.split(".");

    var len = Math.min(a_components.length, b_components.length);

    // loop while the components are equal
    for (var i = 0; i < len; i++) {
        // A bigger than B
        if (parseInt(a_components[i]) > parseInt(b_components[i])) {
            return 1;
        }

        // B bigger than A
        if (parseInt(a_components[i]) < parseInt(b_components[i])) {
            return -1;
        }
    }

    // If one a prefix of the other, the longer one is greater.
    if (a_components.length > b_components.length) {
        return 1;
    }

    if (a_components.length < b_components.length) {
        return -1;
    }

    // Otherwise they are the same.
    return 0;
}

console.log(compare("1", "2"));
console.log(compare("2", "1"));

console.log(compare("1.0", "1.0"));
console.log(compare("2.0", "1.0"));
console.log(compare("1.0", "2.0"));
console.log(compare("1.0.1", "1.0"));

Ответ 4

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

Возвращаемые значения:
- число < 0, если a < б
- число > 0, если a > b
- 0, если a = b

Таким образом, вы можете использовать его как функцию сравнения для Array.sort();

EDIT: Bugfixed Version, зачищающий завершающие нули, чтобы распознать "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"]

Ответ 5

Взято из http://java.com/js/deployJava.js:

    // return true if 'installed' (considered as a JRE version string) is
    // greater than or equal to 'required' (again, a JRE version string).
    compareVersions: function (installed, required) {

        var a = installed.split('.');
        var b = required.split('.');

        for (var i = 0; i < a.length; ++i) {
            a[i] = Number(a[i]);
        }
        for (var i = 0; i < b.length; ++i) {
            b[i] = Number(b[i]);
        }
        if (a.length == 2) {
            a[2] = 0;
        }

        if (a[0] > b[0]) return true;
        if (a[0] < b[0]) return false;

        if (a[1] > b[1]) return true;
        if (a[1] < b[1]) return false;

        if (a[2] > b[2]) return true;
        if (a[2] < b[2]) return false;

        return true;
    }

Ответ 6

Не удалось найти функцию, выполняющую то, что я хотел здесь. Поэтому я написал свой собственный. Это мой вклад. Надеюсь, кто-то найдет это полезным.

Плюсы:

  • Обрабатывает строки версии произвольной длины. '1' или '1.1.1.1.1'.

  • По умолчанию каждое значение равно 0, если не указано. Просто потому, что строка длиннее, это не значит, что это более крупная версия. ('1' должен быть таким же, как "1.0" и "1.0.0.0".)

  • Сравнить номера не строки. ('3'<'21 'должно быть истинным, а не false.)

  • Не тратьте время на бесполезные сравнения в цикле. (Сравнение для ==)

  • Вы можете выбрать свой собственный компаратор.

Минусы:

  • Он не обрабатывает буквы в строке версии. (Я не знаю, как это будет работать?)

Мой код, похожий на принятый ответ Джона:

function compareVersions(v1, comparator, v2) {
    "use strict";
    var comparator = comparator == '=' ? '==' : comparator;
    if(['==','===','<','<=','>','>=','!=','!=='].indexOf(comparator) == -1) {
        throw new Error('Invalid comparator. ' + comparator);
    }
    var v1parts = v1.split('.'), v2parts = v2.split('.');
    var maxLen = Math.max(v1parts.length, v2parts.length);
    var part1, part2;
    var cmp = 0;
    for(var i = 0; i < maxLen && !cmp; i++) {
        part1 = parseInt(v1parts[i], 10) || 0;
        part2 = parseInt(v2parts[i], 10) || 0;
        if(part1 < part2)
            cmp = 1;
        if(part1 > part2)
            cmp = -1;
    }
    return eval('0' + comparator + cmp);
}

<сильные > Примеры:

compareVersions('1.2.0', '==', '1.2'); // true
compareVersions('00001', '==', '1.0.0'); // true
compareVersions('1.2.0', '<=', '1.2'); // true
compareVersions('2.2.0', '<=', '1.2'); // false

Ответ 7

Простая и короткая функция:

function isNewerVersion (oldVer, newVer) {
  const oldParts = oldVer.split('.')
  const newParts = newVer.split('.')
  for (var i = 0; i < newParts.length; i++) {
    const a = parseInt(newParts[i]) || 0
    const b = parseInt(oldParts[i]) || 0
    if (a > b) return true
    if (a < b) return false
  }
  return false
}

тесты:

isNewerVersion('1.0', '2.0') // true
isNewerVersion('1.0', '1.0.1') // true
isNewerVersion('1.0.1', '1.0.10') // true
isNewerVersion('1.0.1', '1.0.1') // false
isNewerVersion('2.0', '1.0') // false
isNewerVersion('2', '1.0') // false
isNewerVersion('2.0.0.0.0.1', '2.1') // true
isNewerVersion('2.0.0.0.0.1', '2.0') // false

Ответ 9

Простите меня, если эта идея уже была посещена в ссылке, которую я не видел.

У меня был некоторый успех при преобразовании частей в взвешенную сумму:

partSum = this.major * Math.Pow(10,9);
partSum += this.minor * Math.Pow(10, 6);
partSum += this.revision * Math.Pow(10, 3);
partSum += this.build * Math.Pow(10, 0);

Который сделал сравнения очень легкими (сравнивая двойной). Наши поля версии не превышают 4 цифр.

7.10.2.184  -> 7010002184.0
7.11.0.1385 -> 7011001385.0

Я надеюсь, что это поможет кому-то, поскольку множественные условности кажутся немного переборщиками.

Ответ 10

Ответ 2017 года:

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

compareVersion(v1,v2) // return true

function compareVersion(ver1, ver2) {
        ver1 = ver1.split('.')
        ver2 = ver2.split('.')
        var i = 0, v1, v2;
        ver1 = Array.isArray(ver1) ? ver1 : [ver1];
        /*default is true*/
        while (i < 4) {
            v1 = ver1[i]; v2 = ver2[i];
            if (!v1 || !v2) return true;
            if (v1 * 1 < v2 * 1) return false;
            i++;
        }
        return true;
    }

Более классный ответ для браузера, который не поддерживает IE (озеро padStart):

 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"//return false, but it wrong "123" "> "099"//but padding the short number with zeros make the comparision to be right

Таким образом, идея решения состоит в том, чтобы заполнить все части версии нулями, чтобы все числа были одинаковой ширины 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

Ответ 11

Вот еще одна короткая версия, которая работает с любым количеством подверсий, дополненных нулями и четными числами с буквами (1.0.0b3)

function compareVer(a, b)
{
    //treat non-numerical characters as lower version
    //replacing them with a negative number based on charcode of each character
    function fix(s)
    {
        return "." + (s.toLowerCase().charCodeAt(0) - 2147483647) + ".";
    }
    a = ("" + a).replace(/[^0-9\.]/g, fix).split('.');
    b = ("" + b).replace(/[^0-9\.]/g, fix).split('.');
    var c = Math.max(a.length, b.length);
    for (var i = 0; i < c; i++)
    {
        //convert to integer the most efficient way
        a[i] = ~~a[i];
        b[i] = ~~b[i];
        if (a[i] > b[i])
            return 1;
        else if (a[i] < b[i])
            return -1;
    }
    return 0;
}

Выход:

0: a = b

1: a> b

-1: a & lt; б

1.0.0.0.0.0 = 1.0
1.0         < 1.0.1
1.0b1       < 1.0
1.0a        < 1.0b
1.1         > 1.0.1b
1.1alpha    < 1.1beta
1.1rc1      > 1.1beta
1.0001      > 1.00000.1.0.0.0.01

/*use strict*/

function compareVer(a, b)
{
    //treat non-numerical characters as lover version
    //replacing them with a negative number based on charcode of each character
    function fix(s)
    {
        return "." + (s.toLowerCase().charCodeAt(0) - 2147483647) + ".";
    }
    a = ("" + a).replace(/[^0-9\.]/g, fix).split('.');
    b = ("" + b).replace(/[^0-9\.]/g, fix).split('.');
    var c = Math.max(a.length, b.length);
    for (var i = 0; i < c; i++)
    {
        //convert to integer the most efficient way
        a[i] = ~~a[i];
        b[i] = ~~b[i];
        if (a[i] > b[i])
            return 1;
        else if (a[i] < b[i])
            return -1;
    }
    return 0;
}

var type = {
  "-1": " < ",
   "0": " = ",
   "1": " > "
};
var list = [
  ["1.0.0.0.0.0", "1.0"],
  ["1.0",         "1.0.1"],
  ["1.0b1",       "1.0"],
  ["1.0a",        "1.0b"],
  ["1.1",         "1.0.1b"],
  ["1.1alpha",    "1.1beta"],
  ["1.1rc1",      "1.1beta"],
  ["1.0001",      "1.00000.1.0.0.0.01"]
];

for(var i = 0; i < list.length; i++)
{
  console.log(list[i][0] + type[compareVer(list[i][0], list[i][1])] + list[i][1]);
}

Ответ 12

// Returns true if v1 is bigger than v2, and false if otherwise.
function isNewerThan(v1, v2) {
      v1=v1.split('.');
      v2=v2.split('.');
      for(var i = 0; i<Math.max(v1.length,v2.length); i++){
        if(v1[i] == undefined) return false; // If there is no digit, v2 is automatically bigger
        if(v2[i] == undefined) return true; // if there is no digit, v1 is automatically bigger
        if(v1[i] > v2[i]) return true;
        if(v1[i] < v2[i]) return false;
      }
      return false; // Returns false if they are equal
    }

Ответ 13

Функция replace() заменяет первое вхождение в строке. Итак, заменим . на ,. Затем удалите все . и снова запустите , до . и проанализируйте его, чтобы он плавал.

for(i=0; i<versions.length; i++) {
    v = versions[i].replace('.', ',');
    v = v.replace(/\./g, '');
    versions[i] = parseFloat(v.replace(',', '.'));
}

наконец, отсортируйте его:

versions.sort();

Ответ 14

Отметьте это сообщение в блоге. Эта функция работает для числовых номеров версий.

function compVersions(strV1, strV2) {
  var nRes = 0
    , parts1 = strV1.split('.')
    , parts2 = strV2.split('.')
    , nLen = Math.max(parts1.length, parts2.length);

  for (var i = 0; i < nLen; i++) {
    var nP1 = (i < parts1.length) ? parseInt(parts1[i], 10) : 0
      , nP2 = (i < parts2.length) ? parseInt(parts2[i], 10) : 0;

    if (isNaN(nP1)) { nP1 = 0; }
    if (isNaN(nP2)) { nP2 = 0; }

    if (nP1 != nP2) {
      nRes = (nP1 > nP2) ? 1 : -1;
      break;
    }
  }

  return nRes;
};

compVersions('10', '10.0'); // 0
compVersions('10.1', '10.01.0'); // 0
compVersions('10.0.1', '10.0'); // 1
compVersions('10.0.1', '10.1'); // -1

Ответ 15

Если, например, мы хотим проверить, будет ли текущая версия jQuery меньше 1,8, parseFloat($.ui.version) < 1.8 ) даст результат неправильный, если версия "1.10.1", поскольку parseFloat ( "1.10.1" ) возвращает 1.1. Сравнение строк также пойдет не так, поскольку "1.8" < "1.10" оценивается как false.

Итак, нам нужен тест вроде этого

if(versionCompare($.ui.version, "1.8") < 0){
    alert("please update jQuery");
}

Следующая функция обрабатывает это правильно:

/** 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 = parseInt(v1parts[i], 10);
        p2 = 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;
}

Вот несколько примеров:

// compare dotted version strings
console.assert(versionCompare("1.8",      "1.8.1")    <   0);
console.assert(versionCompare("1.8.3",    "1.8.1")    >   0);
console.assert(versionCompare("1.8",      "1.10")     <   0);
console.assert(versionCompare("1.10.1",   "1.10.1")   === 0);
// Longer is considered 'greater'
console.assert(versionCompare("1.10.1.0", "1.10.1")   >   0);
console.assert(versionCompare("1.10.1",   "1.10.1.0") <   0);
// Strings pairs are accepted
console.assert(versionCompare("1.x",      "1.x")      === 0);
// Mixed int/string pairs return NaN
console.assert(isNaN(versionCompare("1.8", "1.x")));
//works with plain numbers
console.assert(versionCompare("4", 3)   >   0);

См. здесь для живого образца и набора тестов: http://jsfiddle.net/mar10/8KjvP/

Ответ 16

Здесь реализация coffeescript, подходящая для использования с Array.sort, вдохновленная другими ответами здесь:

# Returns > 0 if v1 > v2 and < 0 if v1 < v2 and 0 if v1 == v2
compareVersions = (v1, v2) ->
  v1Parts = v1.split('.')
  v2Parts = v2.split('.')
  minLength = Math.min(v1Parts.length, v2Parts.length)
  if minLength > 0
    for idx in [0..minLength - 1]
      diff = Number(v1Parts[idx]) - Number(v2Parts[idx])
      return diff unless diff is 0
  return v1Parts.length - v2Parts.length

Ответ 17

Я написал модуль node для сортировки версий, вы можете найти его здесь: version-sort

Функции

  • Без ограничений последовательностей "1.0.1.5.53.54654.114.1.154.45" работает
  • Без ограничения длины последовательности: "1.1546515465451654654654654138754431574364321353734" работает
  • может сортировать объекты по версии (см. README)
  • (например, альфа, бета, rc1, rc2)

Не стесняйтесь открывать проблему, если вам нужна другая функция.

Ответ 18

Это работает для числовых версий любой длины, разделенных точкой. Он возвращает true, только если myVersion >= minimumVersion, делая предположение, что версия 1 меньше 1.0, версия 1.1 меньше 1.1.0 и так далее. Достаточно просто добавить дополнительные условия, такие как принятие чисел (просто преобразование в строку) и шестнадцатеричное число или создание разделителя динамического (просто добавьте параметр разделителя, а затем замените "." На параметр)

function versionCompare(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 true;
        }
        if(Number(v1[i]) < Number(v2[i])) {
            return false;
        }           
    }

    return (v1.length >= v2.length);
}

Вот несколько тестов:

console.log(versionCompare("4.4.0","4.4.1"));
console.log(versionCompare("5.24","5.2"));
console.log(versionCompare("4.1","4.1.2"));
console.log(versionCompare("4.1.2","4.1"));
console.log(versionCompare("4.4.4.4","4.4.4.4.4"));
console.log(versionCompare("4.4.4.4.4.4","4.4.4.4.4"));
console.log(versionCompare("0","1"));
console.log(versionCompare("1","1"));
console.log(versionCompare("","1"));
console.log(versionCompare("10.0.1","10.1"));

Альтернативно здесь рекурсивная версия

function versionCompare(myVersion, minimumVersion) {
  return recursiveCompare(myVersion.split("."),minimumVersion.split("."),Math.min(myVersion.length, minimumVersion.length),0);
}

function recursiveCompare(v1, v2,minLength, index) {
  if(Number(v1[index]) < Number(v2[index])) {
    return false;
  }
  if(Number(v1[i]) < Number(v2[i])) {
    return true;
    }
  if(index === minLength) {
    return (v1.length >= v2.length);
  }
  return recursiveCompare(v1,v2,minLength,index+1);
}

Ответ 19

Идея состоит в том, чтобы сравнить две версии и узнать, какая из них самая большая. Мы удаляем "." и мы сравниваем каждую позицию вектора с другой.

// Return 1  if a > b
// Return -1 if a < b
// Return 0  if a == b

function compareVersions(a_components, b_components) {

   if (a_components === b_components) {
       return 0;
   }

   var partsNumberA = a_components.split(".");
   var partsNumberB = b_components.split(".");

   for (var i = 0; i < partsNumberA.length; i++) {

      var valueA = parseInt(partsNumberA[i]);
      var valueB = parseInt(partsNumberB[i]);

      // A bigger than B
      if (valueA > valueB || isNaN(valueB)) {
         return 1;
      }

      // B bigger than A
      if (valueA < valueB) {
         return -1;
      }
   }
}

Ответ 20

Я нахожу самый простой способ сравнить их, не уверен, что это то, что вы хотите. когда я запускаю код ниже в консоли, это имеет смысл, и с помощью метода sort() я могу получить отсортированный массив строк версий. он основан на алфавитном порядке.

"1.0" < "1.0.1" //true
var arr = ["1.0.1", "1.0", "3.2.0", "1.3"]
arr.sort();     //["1.0", "1.0.1", "1.3", "3.2.0"]

Ответ 21

Мой менее подробный ответ, чем большинство ответов здесь

/**
 * Compare two semver versions. Returns true if version A is greater than
 * version B
 * @param {string} versionA
 * @param {string} versionB
 * @returns {boolean}
 */
export const semverGreaterThan = function(versionA, versionB){
  var versionsA = versionA.split(/\./g),
    versionsB = versionB.split(/\./g)
  while (versionsA.length || versionsB.length) {
    var a = Number(versionsA.shift()), b = Number(versionsB.shift())
    if (a == b)
      continue
    return (a > b || isNaN(b))
  }
  return false
}

Ответ 22

Не могли бы вы преобразовать их в числа, а затем отсортировать после размера? Добавьте 0 к номерам, которые являются < 4 в длину

играл в консоли:

$(["1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1", "3.0"]).each(function(i,e) {
    var n =   e.replace(/\./g,"");
    while(n.length < 4) n+="0" ; 
    num.push(  +n  )
});

больше версия, большее число. Изменить: возможно, нужно настроить для учета больших версий серии

Ответ 23

Это аккуратный трюк. Если вы имеете дело с числовыми значениями, между определенным диапазоном значений, вы можете присвоить значение каждому уровню объекта версии. Например, для параметра "largeValue" установлено значение 0xFF, что создает очень "IP-тип" для вашего управления версиями.

Это также обрабатывает альфа-числовое управление версиями (то есть 1.2a < 1,2b)

// The version compare function
function compareVersion(data0, data1, levels) {
    function getVersionHash(version) {
        var value = 0;
        version = version.split(".").map(function (a) {
            var n = parseInt(a);
            var letter = a.replace(n, "");
            if (letter) {
                return n + letter[0].charCodeAt() / 0xFF;
            } else {
                return n;
            }
        });
        for (var i = 0; i < version.length; ++i) {
            if (levels === i) break;
            value += version[i] / 0xFF * Math.pow(0xFF, levels - i + 1);
        }
        return value;
    };
    var v1 = getVersionHash(data0);
    var v2 = getVersionHash(data1);
    return v1 === v2 ? -1 : v1 > v2 ? 0 : 1;
};
// Returns 0 or 1, correlating to input A and input B
// Direct match returns -1
var version = compareVersion("1.254.253", "1.254.253a", 3);

Ответ 24

Мне нравится версия от @mar10, хотя, с моей точки зрения, есть шанс (кажется, что это не так, если версии совместимы с документом Semantic Versioning, но может быть, если какой-то "номер сборки" б):

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

Таким образом, каждое вспомогательное число, кроме первого, будет сравниваться как float, поэтому 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;
     }
 }

Ответ 25

Я сделал это на основе идеи Kons и оптимизировал его для версии Java "1.7.0_45". Это просто функция, предназначенная для преобразования строки версии в float. Это функция:

function parseVersionFloat(versionString) {
    var versionArray = ("" + versionString)
            .replace("_", ".")
            .replace(/[^0-9.]/g, "")
            .split("."),
        sum = 0;
    for (var i = 0; i < versionArray.length; ++i) {
        sum += Number(versionArray[i]) / Math.pow(10, i * 3);
    }
    console.log(versionString + " -> " + sum);
    return sum;
}

Строка "1.7.0_45" преобразуется в 1.0070000450000001, и это достаточно хорошо для нормального сравнения. Ошибка здесь: Как справиться с точностью чисел с плавающей запятой в JavaScript?. Если вам нужно больше 3 цифр на любой части, вы можете изменить разделитель Math.pow(10, i * 3);.

Результат будет выглядеть следующим образом:

1.7.0_45         > 1.007000045
ver 1.7.build_45 > 1.007000045
1.234.567.890    > 1.23456789

Ответ 26

Это не совсем решение, потому что вопрос был задан, но он очень похож.

Эта функция сортировки предназначена для семантических версий, она обрабатывает разрешенную версию, поэтому она не работает с такими подстановочными знаками, как 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;
}

Ответ 27

У меня была такая же проблема сравнения версий, но с версиями, которые могли содержать что угодно (т.е. разделители, которые не были точками, расширениями, такими как 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

Ответ 28

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

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"

Ответ 29

Еще одна реализация, на которую я полагаю, стоит делиться, поскольку она короткая, простая и вместе с тем мощная. Обратите внимание, что он использует только сравнение цифр. Обычно он проверяет, является ли version2 более поздним, чем version1, и возвращает true, если это случай. Предположим, что у вас есть версия1:1.1.1 и version2: 1.1.2. Он проходит через каждую часть двух версий, добавляя свои части следующим образом: (1 + 0,1), затем (1.1 + 0.01) для версии 1 и (1 + 0,1), затем (1.1 + 0.02) для версии 2.

function compareVersions(version1, version2) {

    version1 = version1.split('.');
    version2 = version2.split('.');

    var maxSubVersionLength = String(Math.max.apply(undefined, version1.concat(version2))).length;

    var reduce = function(prev, current, index) {

        return parseFloat(prev) + parseFloat('0.' + Array(index + (maxSubVersionLength - String(current).length)).join('0') + current);
    };

    return version1.reduce(reduce) < version2.reduce(reduce);
}

Если вы хотите найти последнюю версию из списка версий, это может быть полезно:

function findLatestVersion(versions) {

    if (!(versions instanceof Array)) {
        versions = Array.prototype.slice.apply(arguments, [0]);
    }

    versions = versions.map(function(version) { return version.split('.'); });

    var maxSubVersionLength = String(Math.max.apply(undefined, Array.prototype.concat.apply([], versions))).length;

    var reduce = function(prev, current, index) {

        return parseFloat(prev) + parseFloat('0.' + Array(index + (maxSubVersionLength - String(current).length)).join('0') + current);
    };

    var sums = [];

    for (var i = 0; i < versions.length; i++) {
        sums.push(parseFloat(versions[i].reduce(reduce)));
    }

    return versions[sums.indexOf(Math.max.apply(undefined, sums))].join('.');
}

console.log(findLatestVersion('0.1000000.1', '2.0.0.10', '1.6.10', '1.4.3', '2', '2.0.0.1')); // 2.0.0.10
console.log(findLatestVersion(['0.1000000.1', '2.0.0.10', '1.6.10', '1.4.3', '2', '2.0.0.1'])); // 2.0.0.10

Ответ 30

Вот еще один способ сделать это с помощью рекурсивного алгоритма.

Этот код просто использует Array.shift и рекурсивный, что означает, что он может работать в IE 6+. Если у вас есть какие-либо сомнения, вы можете посетить мой GitHub

(function(root, factory) {
  if (typeof exports === 'object') {
    return module.exports = factory();
  } else if (typeof define === 'function' && define.amd) {
    return define(factory);
  } else {
    return root.compareVer = factory();
  }
})(this, function() {
  'use strict';
  var _compareVer;
  _compareVer = function(newVer, oldVer) {
    var VER_RE, compareNum, isTrue, maxLen, newArr, newLen, newMatch, oldArr, oldLen, oldMatch, zerofill;
    VER_RE = /(\d+\.){1,9}\d+/;
    if (arguments.length !== 2) {
      return -100;
    }
    if (typeof newVer !== 'string') {
      return -2;
    }
    if (typeof oldVer !== 'string') {
      return -3;
    }
    newMatch = newVer.match(VER_RE);
    if (!newMatch || newMatch[0] !== newVer) {
      return -4;
    }
    oldMatch = oldVer.match(VER_RE);
    if (!oldMatch || oldMatch[0] !== oldVer) {
      return -5;
    }
    newVer = newVer.replace(/^0/, '');
    oldVer = oldVer.replace(/^0/, '');
    if (newVer === oldVer) {
      return 0;
    } else {
      newArr = newVer.split('.');
      oldArr = oldVer.split('.');
      newLen = newArr.length;
      oldLen = oldArr.length;
      maxLen = Math.max(newLen, oldLen);
      zerofill = function() {
        newArr.length < maxLen && newArr.push('0');
        oldArr.length < maxLen && oldArr.push('0');
        return newArr.length !== oldArr.length && zerofill();
      };
      newLen !== oldLen && zerofill();
      if (newArr.toString() === oldArr.toString()) {
        if (newLen > oldLen) {
          return 1;
        } else {
          return -1;
        }
      } else {
        isTrue = -1;
        compareNum = function() {
          var _new, _old;
          _new = ~~newArr.shift();
          _old = ~~oldArr.shift();
          _new > _old && (isTrue = 1);
          return _new === _old && newArr.length > 0 && compareNum();
        };
        compareNum();
        return isTrue;
      }
    }
  };
  return _compareVer;
});

Ну, я надеюсь, что этот код поможет кому-то.

Здесь тестирование.

console.log(compareVer("0.0.2","0.0.1"));//1
console.log(compareVer("0.0.10","0.0.1")); //1
console.log(compareVer("0.0.10","0.0.2")); //1
console.log(compareVer("0.9.0","0.9")); //1
console.log(compareVer("0.10.0","0.9.0")); //1
console.log(compareVer("1.7", "1.07")); //1
console.log(compareVer("1.0.07", "1.0.007")); //1

console.log(compareVer("0.3","0.3")); //0
console.log(compareVer("0.0.3","0.0.3")); //0
console.log(compareVer("0.0.3.0","0.0.3.0")); //0
console.log(compareVer("00.3","0.3")); //0
console.log(compareVer("00.3","00.3")); //0
console.log(compareVer("01.0.3","1.0.3")); //0
console.log(compareVer("1.0.3","01.0.3")); //0

console.log(compareVer("0.2.0","1.0.0")); //-1
console.log(compareVer('0.0.2.2.0',"0.0.2.3")); //-1
console.log(compareVer('0.0.2.0',"0.0.2")); //-1
console.log(compareVer('0.0.2',"0.0.2.0")); //-1
console.log(compareVer("1.07", "1.7")); //-1
console.log(compareVer("1.0.007", "1.0.07")); //-1

console.log(compareVer()); //-100
console.log(compareVer("0.0.2")); //-100
console.log(compareVer("0.0.2","0.0.2","0.0.2")); //-100
console.log(compareVer(1212,"0.0.2")); //-2
console.log(compareVer("0.0.2",1212)); //-3
console.log(compareVer('1.abc.2',"1.0.2")); //-4
console.log(compareVer('1.0.2',"1.abc.2")); //-5