Comment comparer le numéro de version du logiciel en utilisant js? (seulement nombre)

164

Voici le numéro de version du logiciel:

"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"

Comment puis-je comparer cela? Supposons que l'ordre correct est:

"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"

L'idée est simple ...: Lisez le premier chiffre, puis le deuxième, après le troisième .... Mais je ne peux pas convertir le numéro de version en numéro flottant .... Vous pouvez également voir le numéro de version comme ce:

"1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1.0"

et c'est plus clair pour voir quelle est l'idée derrière ... Mais, comment le convertir en programme informatique ?? Quelqu'un a-t-il une idée sur la façon de trier cela? Je vous remercie.

Tattat
la source
5
Ce serait une bonne question d'entrevue de type fizzbuzz.
Steve Claridge le
2
C'est pourquoi tous les numéros de version du logiciel doivent être des nombres entiers comme 2001403. Lorsque vous voulez l'afficher d'une manière conviviale comme "2.0.14.3", vous formatez le numéro de version au moment de la présentation.
jarmod
2
Le problème général ici est la comparaison des versions sémantiques, et ce n'est pas trivial (voir # 11 sur semver.org ). Heureusement, il existe une bibliothèque officielle pour cela, le versionneur sémantique pour npm .
Dan Dascalescu
1
Trouvé un script simple qui compare semvers
vsync

Réponses:

133

L'idée de base pour faire cette comparaison serait d'utiliser Array.splitpour obtenir des tableaux de parties à partir des chaînes d'entrée, puis de comparer des paires de parties des deux tableaux; si les parties ne sont pas égales, nous savons quelle version est la plus petite.

Il y a quelques détails importants à garder à l'esprit:

  1. Comment comparer les pièces de chaque paire? La question veut comparer numériquement, mais que se passe-t-il si nous avons des chaînes de version qui ne sont pas composées uniquement de chiffres (par exemple "1.0a")?
  2. Que se passe-t-il si une chaîne de version comporte plus de parties que l'autre? Très probablement "1.0" devrait être considéré comme inférieur à "1.0.1", mais qu'en est-il de "1.0.0"?

Voici le code d'une implémentation que vous pouvez utiliser directement (l' essentiel avec la documentation ):

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

Cette version compare les parties naturellement , n'accepte pas les suffixes de caractères et considère que "1.7" est plus petit que "1.7.0". Le mode de comparaison peut être changé en lexicographique et les chaînes de version plus courtes peuvent être automatiquement complétées à zéro en utilisant le troisième argument facultatif.

Il existe un JSFiddle qui exécute des "tests unitaires" ici ; c'est une version légèrement développée du travail de ripper234 (merci).

Remarque importante: ce code utilise Array.mapet Array.every, ce qui signifie qu'il ne fonctionnera pas dans les versions IE antérieures à 9. Si vous devez prendre en charge ceux-ci, vous devrez fournir des polyfills pour les méthodes manquantes.

Jon
la source
16
Voici une version améliorée avec quelques tests unitaires: jsfiddle.net/ripper234/Xv9WL/28
ripper234
5
Salut à tous, j'ai roulé cet essentiel dans un gitrepo avec des tests et tout et je l'ai mis sur npm et bower afin que je puisse l'inclure plus facilement dans mes projets. github.com/gabe0x02/version_compare
Gabriel Littman
2
@GabrielLittman: Hé, merci d'avoir pris le temps de faire ça! Cependant, tout le code sur SO est sous licence CC-BY-SA par défaut. Cela signifie que vous ne pouvez pas avoir votre package sous licence GPL. Je sais que l'avocat n'est pas la raison pour laquelle quiconque est ici, mais ce serait bien si vous corrigiez le problème.
Jon
2
@GabrielLittman: La GPL est en fait très restrictive dans le sens où vous êtes obligé de mettre sous licence GPL tout le code qui entre en contact avec le code GPL existant. Quoi qu'il en soit, pour référence future: une bonne et largement utilisée licence «faites ce que vous voulez, sans conditions» est le MIT .
Jon
3
@GabrielLittman: il existe déjà des bibliothèques établies écrites par des développeurs chevronnés qui effectuent des comparaisons semver.
Dan Dascalescu
82

semver

L'analyseur de version sémantique utilisé par npm.

$ npm installer 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, '*')

Lien de versionnage sémantique :
https://www.npmjs.com/package/semver#prerelease-identifiers

Mohammed Akdim
la source
8
Oui. C'est la bonne réponse - comparer les versions n'est pas trivial (voir # 11 sur semver.org ), et il existe des bibliothèques de niveau production qui font le travail.
Dan Dascalescu
7
techniquement, ce ne sont pas les bonnes réponses, car node.js et javascript sont différents. J'ai supposé que la question originale était plus ciblée pour le navigateur. Mais Google m'a amené ici et heureusement j'utilise node :)
Lee Gary
2
NodeJS n'est pas seulement une solution uniquement côté serveur. Le framework Electron intègre un nodeJS pour les applications de bureau. C'est en fait la réponse que je cherchais.
Anthony Raymond
2
semver c'est un package npm, il peut être utilisé sur n'importe quel environnement JS! CECI EST la bonne réponse
neiker
4
@artuska bien alors optez simplement pour un autre paquet comme semver-compare - 233B (moins de 0,5kB!) gzipped
:)
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"));
Joe
la source
Je pense que la ligne: var len = Math.min(a_components.length, b_components.length);les versions 2.0.1.1 et 2.0.1 seront-elles traitées comme égales?
Jon Egerton
1
Regarde juste après la boucle! Si une chaîne est un préfixe de l'autre (c'est-à-dire que la boucle atteint la fin), alors la plus longue est considérée comme supérieure.
Joe le
Peut-être avez-vous été découragé par mon trébuchement sur la langue anglaise dans le commentaire ...
Joe
@Joe je sais est une réponse un peu ancienne mais j'utilisais la fonction. Test a = '7'et b = '7.0'retour -1car 7.0 est plus long. Vous avez une suggestion pour ça? ( console.log(compare("7", "7.0")); //returns -1)
RaphaelDDL
Je suppose que cela relève de la rubrique comportement indéfini. Si vous avez ces numéros de version, je suis sûr que vous pouvez modifier la logique en fonction de vos besoins.
Joe
48

Cette fonction de comparaison très petite, mais très rapide, prend des numéros de version de toute longueur et de toute taille de nombre par segment .

Valeurs renvoyées:
- un nombre < 0si a <b
- un nombre > 0si a> b
- 0si a = b

Vous pouvez donc l'utiliser comme fonction de comparaison pour Array.sort ();

EDIT: Version corrigée en supprimant les zéros de fin pour reconnaître "1" et "1.0.0" comme égaux

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

LeJared
la source
Échec avec «0.0» et «0.0.0». Voir violon: jsfiddle.net/emragins/9e9pweqg
emragins
1
@emragins Quand auriez-vous besoin de faire cela?
Skylar Ittner
1
@emragins: Je ne vois pas où ça échoue. Il sort ["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"] où votre code sort ["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"] , ce qui est parfaitement le même, puisque 0.0 et 0.0.0 sont considérés comme égaux , ce qui signifie qu'il est indifférent que «0.0» soit avant «0.0.0» ou vice versa.
LeJared le
Je suis d'accord que c'est un point habituel. J'utilise cela avec github.com/jonmiles/bootstrap-treeview , qui hiérarchise les nœuds d'une manière similaire aux versions, mais ce ne sont en réalité que des nœuds parents / enfants et leurs index. Ex. Parent: 0.0, enfant: 0.0.0, 0.0.1. Voir ce numéro pour plus de détails sur les raisons pour lesquelles je me soucie: github.com/jonmiles/bootstrap-treeview/issues/251
emragins
1
Voir la réponse ici stackoverflow.com/questions/6611824/why-do-we-need-to-use-radix . Les navigateurs plus anciens utilisés pour deviner le paramètre radix s'il n'est pas spécifié. Un zéro non significatif dans une chaîne de nombres comme la partie centrale de "1.09.12" était analysé avec la base = 8, ce qui
donnait le
14

Tiré de 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;
    }
user123444555621
la source
Simple, mais limité à trois champs de version.
Dan Dascalescu
11

Impossible de trouver une fonction faisant ce que je voulais ici. Alors j'ai écrit le mien. C'est ma contribution. J'espère que quelqu'un le trouvera utile.

Avantages:

  • Gère les chaînes de version de longueur arbitraire. «1» ou «1.1.1.1.1».

  • Par défaut, chaque valeur est 0 si elle n'est pas spécifiée. Ce n'est pas parce qu'une chaîne est plus longue que c'est une version plus grande. ('1' doit être identique à '1.0' et '1.0.0.0'.)

  • Comparez les nombres et non les chaînes. («3» <«21» doit être vrai. Pas faux.)

  • Ne perdez pas de temps sur des comparaisons inutiles dans la boucle. (Comparaison pour ==)

  • Vous pouvez choisir votre propre comparateur.

Les inconvénients:

  • Il ne gère pas les lettres de la chaîne de version. (Je ne sais même pas comment cela fonctionnerait?)

Mon code, similaire à la réponse acceptée par Jon :

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

Exemples :

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
Viktor
la source
cette version est à mon avis meilleure que celle de la réponse approuvée!
user3807877
1
Cette fonction est sujette à l'injection de code si le paramètre du comparateur est utilisé avec une entrée utilisateur non cochée! Exemple: compareVersions ('1.2', '== 0; alert ("cotcha");', '1.2');
LeJared
@LeJared True. Quand je l'ai écrit, nous n'allions pas l'utiliser avec le code soumis par l'utilisateur. J'aurais probablement dû en parler comme un con. J'ai maintenant mis à jour le code pour éliminer cette possibilité. Maintenant, cependant, quand webpack et autres bundlers node.js sont devenus courants, je suggérerais que la réponse de Mohammed Akdim ci-dessus, en utilisant semver, serait presque toujours la bonne réponse à cette question.
Viktor
10

Fonction simple et courte:

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
}

Tests:

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
Arthur Araújo
la source
Vous pouvez le simplifier avec: const a = ~~ newParts [i]; En fait, c'est le moyen le plus efficace de convertir une chaîne en un entier, qui renvoie 0 si la variable n'est pas définie ou contient des caractères non numériques.
vanowm
5

Pardonnez-moi si cette idée a déjà été visitée dans un lien que je n'ai pas vu.

J'ai eu un certain succès avec la conversion des parties en une somme pondérée comme ceci:

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

Ce qui a rendu les comparaisons très faciles (comparer un double). Nos champs de version ne comportent jamais plus de 4 chiffres.

7.10.2.184  -> 7010002184.0
7.11.0.1385 -> 7011001385.0

J'espère que cela aide quelqu'un, car les multiples conditions semblent un peu exagérées.

Noxin
la source
2
Cela cassera, si this.minor> 999 (chevauchera avec majeur)
Afanasii Kurakin
5

Voici une autre version courte qui fonctionne avec n'importe quel nombre de sous-versions, des zéros remplis et des nombres pairs avec des lettres (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;
}

Production:

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/

vanowm
la source
5

Réponse 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;
    }

Code le plus simple pour les navigateurs modernes:

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

L'idée ici est de comparer des nombres mais sous forme de chaîne. pour que la comparaison fonctionne, les deux chaînes doivent avoir la même longueur. alors:

"123" > "99" devenir "123" > "099"
remplissage du nombre court "fixer" la comparaison

Ici, je remplis chaque partie avec des zéros à des longueurs de 10. puis utilisez simplement une simple chaîne de comparaison pour la réponse

Exemple :

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
pery mimon
la source
expliqueriez-vous compareVersion2ce qui se passe exactement?
Usman Wali
Bon, alors vous pouvez utiliser à la substringplace de padStartpour une meilleure compatibilité ie var zeros = "0000000000"; '0.2.32'.split('.').map( s => zeros.substring(0, zeros.length-s.length) + s ).join('.') vous donnera 0000000000.0000000002.0000000032:)
Usman Wali
4

Ma réponse moins verbeuse que la plupart des réponses ici

/**
 * 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
}
Allié
la source
1
vous devriez en faire un module et le mettre sur node.js. jusque-là, je vole votre code avec attribution à vous. Merci pour ça.
r3wt
3

Bien que cette question ait déjà beaucoup de réponses, chacun promeut sa propre solution de brasserie, alors que nous avons tout un écosystème de bibliothèques testées (au combat) pour cela.

Une recherche rapide sur NPM , GitHub , X nous donnera quelques belles bibliothèques, et je voudrais en parcourir quelques-unes:

semver-compareest une excellente bibliothèque légère (~ 230B) particulièrement utile si vous voulez trier par numéro de version, comme le retourne la méthode exposée de la bibliothèque -1, 0ou de 1manière appropriée.

Le noyau de la lib:

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 est plutôt volumineux (~ 4,4 ko gzippé), mais permet de belles comparaisons uniques comme pour trouver le min / max d'une pile de versions ou pour savoir si la version fournie est unique ou inférieure à toute autre chose dans une collection de versions.

compare-versionsest une autre petite bibliothèque (~ 630B gzippée) et suit bien les spécifications, ce qui signifie que vous pouvez comparer les versions avec des indicateurs alpha / bêta et même des caractères génériques (comme pour les versions mineures / correctives: 1.0.xou1.0.* )

Le fait est qu'il n'est pas toujours nécessaire de copier-coller du code à partir de StackOverflow, si vous pouvez trouver des versions décentes testées (à l'unité) via le gestionnaire de packages de votre choix.

kano
la source
3

J'ai été confronté à un problème similaire et j'avais déjà créé une solution pour cela. N'hésitez pas à l'essayer.

Il renvoie 0pour equal, 1si la version est greateret -1si elle estless

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

Super développeur
la source
2

L'idée est de comparer deux versions et de savoir quelle est la plus grosse. Nous supprimons "." et nous comparons chaque position du vecteur avec l'autre.

// 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;
      }
   }
}
Marc
la source
Réponse épique, exactement ce que je cherchais.
Vince le
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
    }
Dyllan M
la source
1
Bienvenue à SO. Cette question a déjà beaucoup de bonnes réponses, veuillez ne pas ajouter de nouvelles réponses sauf si vous ajoutez quelque chose de nouveau.
poste
1

La replace()fonction remplace uniquement la première occurrence de la chaîne. Alors, remplaçons le .par ,. Ensuite, supprimez tout .et refaites le ,to .et analysez-le pour qu'il flotte.

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

enfin, triez-le:

versions.sort();
Galère Sascha
la source
1

Consultez ce billet de blog . Cette fonction fonctionne pour les numéros de version numériques.

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
David
la source
1

Si, par exemple, nous voulons vérifier si la version actuelle de jQuery est inférieure à 1.8, parseFloat($.ui.version) < 1.8 )donnerait un résultat erroné si la version est "1.10.1", puisque parseFloat ("1.10.1") retourne 1.1. Une comparaison de chaînes serait également erronée, car elle est "1.8" < "1.10"évaluée àfalse .

Nous avons donc besoin d'un test comme celui-ci

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

La fonction suivante gère cela correctement:

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

Voici quelques exemples:

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

Voir ici pour un échantillon en direct et une suite de tests: http://jsfiddle.net/mar10/8KjvP/

mars10
la source
arghh, vient de remarquer que ripper234 avait posté une URL de violon sur e des commentaires il y a quelques mois qui est assez similaire. Bref, je garde ma réponse ici ...
mar10
Celui-ci échouera également (comme la plupart des variantes autour) dans ces cas: versionCompare ('1.09', '1.1') renvoie "1", de la même manière que versionCompare ('1.702', '1.8').
shaman.sir
Le code évalue "1.09"> "1.1" et "1.702"> "1.8", ce que je pense est correct. Si vous n'êtes pas d'accord: pouvez-vous indiquer une ressource qui étaye votre opinion?
mar10
Cela dépend de vos principes - car je sais qu'il n'y a pas de règle stricte ou quelque chose comme ça. En ce qui concerne les ressources, l'article de Wikipédia sur «Versionnage du logiciel» dans «Séquences d'incrémentation» indique que la 1.81 peut être une version mineure de la 1.8, donc la 1.8 doit se lire comme 1.80. L'article sur le versionnage sémantique semver.org/spec/v2.0.0.html dit également que 1.9.0 -> 1.10.0 -> 1.11.0, donc 1.9.0 est traité comme 1.90.0 en comparaison comme ceci. Donc, suivant cette logique, la version 1.702 était antérieure à la version 1.8, qui est traitée comme 1.800.
shaman.sir
1
Je vois que certaines règles traitent 1,8 <1,81 <1,9. Mais en semver, vous utiliseriez 1.8.1 au lieu de 1.81. Semver (si je comprends bien) est défini autour de l'hypothèse que l'incrémentation d'une pièce générera toujours une version `` ultérieure '', donc 1,8 <1,8,1 <1,9 <1,10 <1,81 <1,90 <1,100. Je ne vois pas non plus d'indication que cela soit limité à deux chiffres. Donc je dirais que mon code est entièrement compatible avec semver.
mars 10
1

Voici une implémentation de coffeescript adaptée à une utilisation avec Array.sort inspirée par d'autres réponses ici:

# 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
LOAS
la source
Ceci est inspiré par la réponse de LeJared .
Dan Dascalescu
cela ne fonctionne pas correctement .. voici le résultat .. résultat ['1.1.1', '2.1.1', '3.3.1.0', '3.1.1.0']
ertan2002
1

J'ai écrit un module de nœud pour trier les versions, vous pouvez le trouver ici: version-sort

Caractéristiques :

  • aucune limite de séquences '1.0.1.5.53.54654.114.1.154.45' fonctionne
  • pas de limite de longueur de séquence: '1.1546515465451654654654654138754431574364321353734' fonctionne
  • peut trier les objets par version (voir README)
  • étapes (comme alpha, bêta, rc1, rc2)

N'hésitez pas à ouvrir un problème si vous avez besoin d'une autre fonctionnalité.

Quentin Rossetti
la source
1

Cela fonctionne pour les versions numériques de toute longueur séparées par un point. Il renvoie true uniquement si myVersion est> = minimumVersion, en supposant que la version 1 est inférieure à 1.0, la version 1.1 est inférieure à 1.1.0 et ainsi de suite. Il devrait être assez simple d'ajouter des conditions supplémentaires telles que l'acceptation de nombres (simplement convertir en chaîne) et hexadécimal ou rendre le délimiteur dynamique (il suffit d'ajouter un paramètre de délimiteur puis de remplacer le "." Par le paramètre)

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

Voici quelques tests:

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

Sinon, voici une version récursive

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);
}
Will Schulz
la source
1

Je trouve le moyen le plus simple de les comparer, je ne sais pas si c'est ce que vous voulez. lorsque j'exécute sous le code dans la console, cela a du sens, et en utilisant la méthode sort (), je pourrais obtenir le tableau trié de la chaîne de versions. il est basé sur l'ordre alphabétique.

"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"]
inoutwhy
la source
3
Cela ne fonctionne pas bien pour les numéros de version à deux chiffres, par exemple 1.10.0.
Leukipp
1

Vous pouvez utiliser String#localeCompareavecoptions

sensibilité

Quelles différences dans les chaînes doivent conduire à des valeurs de résultat non nulles. Les valeurs possibles sont:

  • "base": Seules les chaînes dont les lettres de base diffèrent sont considérées comme inégales. Exemples: a ≠ b, a = á, a = A.
  • "accent": Seules les chaînes dont les lettres de base ou les accents et les autres signes diacritiques diffèrent sont considérées comme inégales. Exemples: a ≠ b, a ≠ á, a = A.
  • "case": Seules les chaînes qui diffèrent par les lettres de base ou la casse sont considérées comme inégales. Exemples: a ≠ b, a = á, a ≠ A.
  • "variant": Les chaînes qui diffèrent par les lettres de base, les accents et autres signes diacritiques, ou la casse se comparent comme étant inégales. D'autres différences peuvent également être prises en considération. Exemples: a ≠ b, a ≠ á, a ≠ A.

La valeur par défaut est "variant" pour l'utilisation "sort"; il dépend de la localisation pour l'utilisation "recherche".

numérique

Indique si le classement numérique doit être utilisé, tel que "1" <"2" <"10". Les valeurs possibles sont trueet false; la valeur par défaut est false. Cette option peut être définie via une propriété options ou via une clé d'extension Unicode; si les deux sont fournis, la optionspropriété prévaut. Les implémentations ne sont pas requises pour prendre en charge cette propriété.

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

Nina Scholz
la source
Comment cela fonctionne-t-il réellement? Quelle est la undefinedlangue ci - dessus? Comment se fait-il que vous parveniez à poster ceci pendant que je lis les autres;)
mplungjan
undefinedest la partie locale, n'est pas utilisée ici.
Nina Scholz
0

ne pourriez-vous pas les convertir en nombres et ensuite trier après la taille? Ajouter des 0 à ceux des nombres de <4 de longueur

joué dans la console:

$(["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  )
});

plus la version est grande, plus le nombre est grand. Edit: doit probablement être ajusté pour tenir compte des séries de versions plus grandes

Contra
la source
C'était juste un exemple, car il doit faire certaines choses lui-même: P Au lieu de 4, obtenez le nombre de nombres de la version la plus grande, puis remplissez ceux qui sont inférieurs à cela avec des 0
Contra
0

C'est un truc sympa. Si vous avez affaire à des valeurs numériques, entre une plage de valeurs spécifique, vous pouvez attribuer une valeur à chaque niveau de l'objet version. Par exemple, "plus grandValeur" est défini ici sur 0xFF, ce qui crée une sorte de regard très "IP" sur votre versioning.

Cela gère également la gestion des versions alphanumériques (ie 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);
Michael Deal
la source
0

J'aime la version de @ mar10 , bien que de mon point de vue, il y ait un risque d'utilisation abusive (il semble que ce ne soit pas le cas si les versions sont compatibles avec le document de version sémantique , mais cela peut être le cas si un "numéro de build" est utilisé ):

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

Le problème ici est que les sous-numéros du numéro de version sont, dans certains cas, écrits avec des zéros de fin coupés (du moins comme je le vois récemment en utilisant un logiciel différent), ce qui est similaire à la partie rationnelle d'un nombre, donc:

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

Le premier (ou les deux premier et deuxième) sous-numéro de version, cependant, est toujours traité comme une valeur entière à laquelle il est en fait égal.

Si vous utilisez ce type de gestion des versions, vous pouvez modifier quelques lignes dans l'exemple:

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

Ainsi, chaque sous-nombre sauf le premier sera comparé comme un flottant, ainsi 09et 1deviendra 0.09et en 0.1conséquence et comparé correctement de cette façon. 2054et 3deviendra 0.2054et0.3 .

La version complète est donc (crédits à @ 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 Il est plus lent, mais aussi possible de penser à réutiliser la même fonction de comparaison en opérant le fait que la chaîne est en fait le tableau de caractères:

 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;
     }
 }
shaman.sir
la source
0

J'ai fait ceci basé sur l'idée de Kons, et l'ai optimisé pour la version Java "1.7.0_45". C'est juste une fonction destinée à convertir une chaîne de version en float. Voici la fonction:

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

La chaîne "1.7.0_45" est convertie en 1.0070000450000001 et cela suffit pour une comparaison normale. Erreur expliquée ici: Comment gérer la précision des nombres à virgule flottante en JavaScript? . Si besoin de plus de 3 chiffres sur n'importe quelle pièce, vous pouvez changer le diviseurMath.pow(10, i * 3); .

La sortie ressemblera à ceci:

1.7.0_45         > 1.007000045
ver 1.7.build_45 > 1.007000045
1.234.567.890    > 1.23456789
Adrian S
la source
0

J'ai eu le même problème de comparaison de versions, mais avec des versions contenant peut-être n'importe quoi (ie: séparateurs qui n'étaient pas des points, extensions comme rc1, rc2 ...).

J'ai utilisé ceci, qui divise essentiellement les chaînes de version en nombres et non-nombres, et essaie de comparer en fonction du type.

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

Il y a quelques hypothèses ici pour certains cas, par exemple: "1.01" === "1.1", ou "1.8" <"1.71". Il ne parvient pas à gérer "1.0.0-rc.1" <"1.0.0", comme spécifié par Semantic versionning 2.0.0

Uriel
la source
0

Le prétraitement des versions avant le tri signifie que parseInt n'est pas appelé plusieurs fois inutilement. En utilisant Array # map similaire à la suggestion de Michael Deal, voici un tri que j'utilise pour trouver la dernière version d'un semver standard en 3 parties:

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"

theGecko
la source