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


164

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

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

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


5
Это был бы хороший вопрос интервью типа fizzbuzz.
Стив Кларидж

2
Вот почему все номера версий программного обеспечения должны быть целыми числами, такими как 2001403. Если вы хотите отобразить его некоторым дружественным способом, таким как «2.0.14.3», тогда вы форматируете номер версии во время презентации.
Джармод

2
Общая проблема здесь - это сравнение семантической версии, и оно нетривиально (см. # 11 на semver.org ). К счастью, для этого есть официальная библиотека, семантический версионер для npm .
Дан Даскалеску

1
Нашел простой скрипт, который сравнивает semvers
vsync

Ответы:


133

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

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

  1. Как следует сравнивать детали в каждой паре? Вопрос хочет сравнить численно, но что, если у нас есть строки версий, которые не состоят только из цифр (например, «1.0a»)?
  2. Что должно произойти, если одна строка версии имеет больше частей, чем другая? Скорее всего, «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.every, что означает, что он не будет работать в версиях IE более ранних, чем 9. Если вам потребуется их поддержка, вам придется предоставить полифилы для отсутствующих методов.


16
Вот улучшенная версия с некоторыми юнит-тестами: jsfiddle.net/ripper234/Xv9WL/28
ripper234

5
Привет всем, я свернул эту гистограмму в гитрепо с тестами и всем остальным и поставил его на npm и bower, чтобы мне было легче включать его в мои проекты. github.com/gabe0x02/version_compare
Габриэль Литтман

2
@GabrielLittman: Эй, спасибо, что нашли время сделать это! Однако весь код на SO лицензируется с CC-BY-SA по умолчанию. Это означает, что вы не можете иметь лицензию GPL. Я знаю, что юристы - это не то, для чего здесь кто-то есть, но было бы хорошо, если бы вы исправили это.
Джон

2
@GabrielLittman: GPL на самом деле очень ограничен в том смысле, что вы вынуждены лицензировать GPL весь код, который вступает в контакт с существующим кодом GPL. В любом случае, для дальнейшего использования: хорошая и широко используемая лицензия «делай, что хочешь, без ограничений» - это MIT .
Джон

3
@GabrielLittman: уже существуют созданные библиотеки, написанные опытными разработчиками, которые проводят сравнительные сравнения.
Дан Даскалеску

82

semver

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

$ npm установить семвер

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


8
Да. Это правильный ответ - сравнивать версии нетривиально (см. # 11 на semver.org ), и есть библиотеки производственного уровня, которые делают эту работу.
Дан Даскалеску

7
технически это не правильные ответы, так как node.js и javascript разные. Я предположил, что оригинальный вопрос был нацелен больше на браузер. Но Google привел меня сюда, и, к счастью, я использую нод :)
Ли Гари

2
NodeJS - это не только решение на стороне сервера. Электронная структура встраивает узел JS для настольных приложений. Это на самом деле ответ, который я искал.
Энтони Рэймонд

2
semver это пакет npm, его можно использовать в любой среде JS! Это правильный ответ
neiker

4
@artuska хорошо, тогда просто перейдите на другой пакет, например, semver-Compare - 233B (меньше 0,5 КБ!) gzipped:)
kano

50
// 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's 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"));

Я думаю, что строка: var len = Math.min(a_components.length, b_components.length);приведет к тому, что версии 2.0.1.1 и 2.0.1 будут рассматриваться как равные, не так ли?
Джон Эгертон

1
Посмотри сразу после петли! Если одна строка является префиксом другой (т. Е. Цикл достигает конца), то более длинная считается более высокой.
Джо

Возможно, вы откладывали мой камень над английским языком в комментарии ...
Джо

@ Джо, я знаю, это старый ответ, но я использовал эту функцию. Тестирование a = '7'и b = '7.0'возврат, -1потому что 7.0 длиннее. Есть какие-нибудь предложения для этого? ( console.log(compare("7", "7.0")); //returns -1)
RaphaelDDL

Я предполагаю, что это относится к неопределенному поведению. Если у вас есть эти номера версий, то я уверен, что вы можете изменить логику в соответствии с вашими требованиями.
Джо

48

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

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

Таким образом, вы можете использовать его как функцию сравнения для 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"]


Ошибка с «0.0» и «0.0.0». Смотрите скрипку: jsfiddle.net/emragins/9e9pweqg
emragins

1
@emragins Когда тебе нужно это сделать?
Скайлар Итнер

1
@emragins: я не вижу, где это терпит неудачу. Он выводит ["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"] там, где выводит ваш код ["0.0", "0.0.0", "0.4.1", "0.5", "1", "1.0.0", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"] , что совершенно одинаково, поскольку 0.0 и 0.0.0 считаются равными , что означает, что не имеет значения, является ли «0.0» перед «0.0.0» или наоборот.
LeJared

Я согласен, что это обычная точка зрения. Я использую это с github.com/jonmiles/bootstrap-treeview , который разделяет узлы способом, аналогичным версиям, только на самом деле это просто родительские / дочерние узлы и их индексы. Ex. Родитель: 0,0, ребенок: 0,0,0, 0,0,1. См. Эту проблему для получения более подробной информации о том, почему я забочусь: github.com/jonmiles/bootstrap-treeview/issues/251
emragins

1
Смотрите ответ здесь stackoverflow.com/questions/6611824/why-do-we-need-to-use-radix . В старых браузерах угадывался параметр radix, если он не указан. Начальный ноль в числовой строке, такой как средняя часть в «1.09.12», использовался для анализа с помощью radix = 8, в результате чего
получилось

14

Взято с 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;
    }

Простой, но ограниченный тремя полями версии.
Дан Даскалеску

11

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

Плюсы:

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

  • По умолчанию каждое значение равно 0, если не указано. То, что строка длиннее, не означает, что она больше. («1» должно совпадать с «1.0» и «1.0.0.0».)

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

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

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

Минусы:

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

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

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

эта версия, на мой взгляд, лучше, чем в утвержденном ответе!
user3807877

1
Эта функция подвержена внедрению кода, если параметр компаратора используется с неконтролируемым пользовательским вводом! Пример: compareVersions ('1.2', '== 0; alert ("cotcha");', '1.2');
LeJared

@ LeJared Правда. Когда я написал это, мы не собирались использовать его с кодом, представленным пользователем. Наверное, должен был поднять это как мошенничество. Сейчас я обновил код, чтобы исключить эту возможность. Однако теперь, когда распространены webpack и другие узлы node.js, я бы предположил, что приведенный выше ответ Мохаммеда Акдима с использованием semver почти всегда будет правильным ответом на этот вопрос.
Виктор

10

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

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

Вы можете упростить это с помощью: const a = ~~ newParts [i]; Фактически это наиболее эффективный способ преобразования строки в целое число, которое возвращает 0, если переменная не определена или содержит нечисловые символы.
Ванов

5

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

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

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

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


2
Это сломается, если this.minor> 999 (будет совпадать с мажором)
Афанасий Куракин

5

Вот еще одна короткая версия, которая работает с любым количеством подверсий, дополненными нулями и даже числами с буквами (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 <b

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

https://jsfiddle.net/vanowm/p7uvtbor/


5

Ответ 2017 года:

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

compareVersions(v1,v2) 
// return positive: v1 > v2, zero:v1 == v2, negative: v1 < v2 
function compareVersions(v1, v2) {
        v1= v1.split('.')
        v2= v2.split('.')
        var len = Math.max(v1.length,v2.length)
        /*default is true*/
        for( let i=0; i < len; i++)
            v1 = Number(v1[i] || 0);
            v2 = Number(v2[i] || 0);
            if (v1 !== v2) return v1 - v2 ;
            i++;
        }
        return 0;
    }

Простейший код для современных браузеров:

 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" становиться "123" > "099"
короткий номер "исправить" сравнение

Здесь я дополняю каждую часть нулями до длины 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

Вы бы объяснили функцию, compareVersion2что именно происходит?
Усман Вали

Хорошо, тогда вы можете использовать substringвместо padStartлучшей совместимости, т.е. var zeros = "0000000000"; '0.2.32'.split('.').map( s => zeros.substring(0, zeros.length-s.length) + s ).join('.') даст вам 0000000000.0000000002.0000000032:)
Усман Вали


4

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

/**
 * 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
}

1
Вы должны сделать его модулем и поместить его в node.js. до тех пор я краду ваш код с указанием ссылки на вас. Спасибо тебе за это.
r3wt

3

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

Быстрый поиск по 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.4kB в сжатом виде), но позволяет провести несколько приятных уникальных сравнений, таких как поиск минимума / максимума в стеке версий или выяснение, является ли предоставленная версия уникальной или меньше, чем что-либо еще в коллекции версии.

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

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


3

Я столкнулся с подобной проблемой, и я уже создал решение для нее. Не стесняйтесь попробовать.

Возвращает 0для equal, 1если версия greaterи -1если этоless

function compareVersion(currentVersion, minVersion) {
  let current = currentVersion.replace(/\./g," .").split(' ').map(x=>parseFloat(x,10))
  let min = minVersion.replace(/\./g," .").split(' ').map(x=>parseFloat(x,10))

  for(let i = 0; i < Math.max(current.length, min.length); i++) {
    if((current[i] || 0) < (min[i] || 0)) {
      return -1
    } else if ((current[i] || 0) > (min[i] || 0)) {
      return 1
    }
  }
  return 0
}


console.log(compareVersion("81.0.1212.121","80.4.1121.121"));
console.log(compareVersion("81.0.1212.121","80.4.9921.121"));
console.log(compareVersion("80.0.1212.121","80.4.9921.121"));
console.log(compareVersion("4.4.0","4.4.1"));
console.log(compareVersion("5.24","5.2"));
console.log(compareVersion("4.1","4.1.2"));
console.log(compareVersion("4.1.2","4.1"));
console.log(compareVersion("4.4.4.4","4.4.4.4.4"));
console.log(compareVersion("4.4.4.4.4.4","4.4.4.4.4"));
console.log(compareVersion("0","1"));
console.log(compareVersion("1","1"));
console.log(compareVersion("1","1.0.00000.0000"));
console.log(compareVersion("","1"));
console.log(compareVersion("10.0.1","10.1"));


2

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

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

Эпический ответ, именно то, что я искал.
Винс

2
// 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
    }

1
Добро пожаловать в ТАК. На этот вопрос уже есть много хороших ответов, пожалуйста, воздержитесь от добавления новых ответов, если вы не добавите что-то новое.
внутр

1

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

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

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

versions.sort();

1

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

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

1

Если, например, мы хотим проверить, является ли текущая версия 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/


ааааа, только что заметил, что ripper234 опубликовал URL-адрес скрипки на комментариях несколько месяцев назад, что очень похоже. Во всяком случае, я держу свой ответ здесь ...
марта

В этом случае также произойдет сбой (как и в большинстве других вариантов): versionCompare ('1.09', '1.1') возвращает «1», так же, как versionCompare («1.702», «1.8»).
shaman.sir

Код оценивает "1.09"> "1.1" и "1.702"> "1.8", что я считаю правильным. Если вы не согласны: можете ли вы указать на какой-либо ресурс, который поддерживает ваше мнение?
марта

Это зависит от ваших принципов - насколько я знаю, здесь нет строгих правил или чего-то подобного. Что касается ресурсов, в статье в Википедии «Версии программного обеспечения» в «Последовательности увеличения» говорится, что 1.81 может быть второстепенной версией 1.8, поэтому 1.8 следует читать как 1.80. В статье о семантическом управлении версиями semver.org/spec/v2.0.0.html также говорится, что 1.9.0 -> 1.10.0 -> 1.11.0, поэтому 1.9.0 рассматривается как 1.90.0 в сравнении, как это. Таким образом, следуя этой логике, версия 1.702 была до версии 1.8, которая рассматривается как 1.800.
shaman.sir

1
Я вижу, что некоторые правила относятся к 1,8 <1,81 <1,9. Но в semver вы бы использовали 1.8.1 вместо 1.81. Semver (насколько я понимаю) определяется вокруг предположения о том, что при увеличении детали всегда будет создаваться «более поздняя» версия, поэтому 1.8 <1.8.1 <1.9 <1.10 <1.81 <1.90 <1.100. Я не вижу признаков того, что это также ограничивается двумя цифрами. Так что я бы сказал, что мой код полностью соответствует semver.
марта

1

Вот реализация 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


это не работает должным образом .. вот результат .. результат ['1.1.1', '2.1.1', '3.3.1.0', '3.1.1.0']
ertan2002

1

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

Особенности :

  • без ограничений последовательностей '1.0.1.5.53.54654.114.1.154.45' работает
  • нет предела длины последовательности: '1.1546515465451654654654654138754431574364321353734' работает
  • можно сортировать объекты по версии (см. README)
  • этапы (например, альфа, бета, RC1, RC2)

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


1

Это работает для числовых версий любой длины, разделенных точкой. Он возвращает true только в том случае, если myVersion> >imumVersion>, предполагая, что версия 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);
}

1

Я нахожу самый простой способ сравнить их, не уверен, что это то, что вы хотите. когда я запускаю приведенный ниже код в консоли, это имеет смысл, и, используя метод 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"]

3
Он не работает для двузначных номеров версий, например, 1.10.0.
Леукипп

1

Вы могли бы использовать String#localeCompareсoptions

чувствительность

Какие различия в строках должны привести к ненулевым значениям результата. Возможные значения:

  • "base": Только строки, отличающиеся базовыми буквами, сравниваются как неравные. Примеры: a ≠ b, a = á,a = A .
  • "accent": Только строки, которые отличаются базовыми буквами или акцентами и другими диакритическими знаками, сравниваются как неравные. Примеры: a ≠ b, a ≠ á, a = A.
  • "case": Только строки, которые отличаются базовыми буквами или регистром, сравниваются как неравные. Примеры: a ≠ b, a = á, a ≠ A.
  • "variant": Строки, которые отличаются базовыми буквами, ударениями и другими диакритическими знаками или регистром, сравниваются как неравные. Другие различия также могут быть приняты во внимание. Примеры: a ≠ b, a ≠ á, a ≠ A.

По умолчанию это «вариант» для использования «сортировка»; это зависит от локали использования "поиск".

числовой

Нужно ли использовать числовое сопоставление, чтобы "1" <"2" <"10". Возможные значения trueи false; по умолчанию это false. Эта опция может быть установлена ​​через свойство options или через ключ расширения Unicode; если оба предоставлены, optionsсвойство имеет приоритет. Реализации не обязаны поддерживать это свойство.

var versions = ["2.0.1", "2.0", "1.0", "1.0.1", "2.0.0.1"];

versions.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }));

console.log(versions);


Как это на самом деле работает? Что undefinedвыше, язык? Почему вы можете опубликовать это, пока я читаю остальных;)
mplungjan

undefinedявляется частью локали, здесь не используется.
Нина Шольц

0

Не могли бы вы преобразовать их в числа, а затем отсортировать по размеру? Добавьте 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  )
});

Чем больше версия, тем больше число. Редактировать: вероятно, необходимо настроить для учета большей серии версий


Это был просто пример, так как он сам должен что-то сделать: P Вместо 4, получите количество чисел, которое есть в самой большой версии, затем заполните цифры меньше 0
Contra

0

Это изящный трюк. Если вы имеете дело с числовыми значениями, между определенным диапазоном значений вы можете назначить значение каждому уровню объекта версии. Например, "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);

0

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

0

Я сделал это, основываясь на идее Kons, и оптимизировал ее для версии Java 1.7.0_45. Это просто функция, предназначенная для преобразования строки версии в число с плавающей точкой. Это функция:

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

0

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


0

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.