Come confrontare il numero di versione del software usando js? (solo numero)


164

Ecco il numero di versione del software:

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

Come posso confrontare questo ?? Supponiamo che l'ordine corretto sia:

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

L'idea è semplice ...: leggi la prima cifra, poi la seconda, dopo la terza .... Ma non riesco a convertire il numero di versione in numero float .... Puoi anche vedere il numero di versione come Questo:

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

e questo è più chiaro per vedere qual è l'idea alla base ... Ma come convertirlo in un programma per computer ?? Qualcuno ha qualche idea su come ordinare questo? Grazie.


5
Questa sarebbe una buona domanda di intervista di tipo fizzbuzz.
Steve Claridge,

2
Questo è il motivo per cui tutti i numeri di versione del software devono essere numeri interi come 2001403. Quando si desidera visualizzarlo in modo intuitivo come "2.0.14.3", è necessario formattare il numero di versione al momento della presentazione.
jarmod,

2
Il problema generale qui sono i confronti della versione semantica, ed è non banale (vedi # 11 su semver.org ). Fortunatamente, esiste una libreria ufficiale per questo, il versioner semantico per npm .
Dan Dascalescu,

1
Ho trovato un semplice script che confronta semestri
vsync il

Risposte:


133

L'idea di base per fare questo confronto sarebbe quella di usare Array.splitper ottenere array di parti dalle stringhe di input e quindi confrontare coppie di parti dai due array; se le parti non sono uguali, sappiamo quale versione è più piccola.

Ci sono alcuni dettagli importanti da tenere a mente:

  1. Come devono essere confrontate le parti di ciascuna coppia? La domanda vuole fare un confronto numerico, ma cosa succede se abbiamo stringhe di versione che non sono composte da sole cifre (ad es. "1.0a")?
  2. Cosa dovrebbe succedere se una stringa di versione ha più parti dell'altra? Molto probabilmente "1.0" dovrebbe essere considerato inferiore a "1.0.1", ma che dire di "1.0.0"?

Ecco il codice per un'implementazione che puoi usare direttamente (in sintesi con la documentazione ):

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

Questa versione confronta le parti in modo naturale , non accetta suffissi di carattere e considera "1.7" inferiore a "1.7.0". La modalità di confronto può essere cambiata in lessico e le stringhe di versione più brevi possono essere automaticamente riempite di zero usando il terzo argomento opzionale.

C'è un JSFiddle che esegue "unit test" qui ; è una versione leggermente ampliata del lavoro di ripper234 (grazie).

Nota importante: questo codice utilizza Array.mape Array.every, il che significa che non verrà eseguito nelle versioni di IE precedenti alla 9. Se è necessario supportarle, è necessario fornire polyfill per i metodi mancanti.


16
Ecco una versione migliorata con alcuni test unitari: jsfiddle.net/ripper234/Xv9WL/28
ripper234

5
Ciao a tutti, ho trasformato questo succo in un gitrepo con test e tutto e l'ho messo su npm e bower in modo da poterlo includere più facilmente nei miei progetti. github.com/gabe0x02/version_compare
Gabriel Littman,

2
@GabrielLittman: Ehi, grazie per aver dedicato del tempo per farlo! Tuttavia, tutto il codice su SO è concesso in licenza con CC-BY-SA per impostazione predefinita. Ciò significa che non puoi avere il tuo pacchetto con licenza GPL. So che legiferare non è ciò per cui nessuno è qui, ma sarebbe positivo se lo risolvessi.
Jon

2
@GabrielLittman: GPL è in realtà molto restrittivo, nel senso che sei costretto a concedere in licenza GPL tutto il codice che viene a contatto con il codice GPL esistente. Ad ogni modo, per riferimento futuro: una licenza valida e ampiamente utilizzata "fai tutto quello che vuoi, senza stringhe" è il MIT .
Jon,

3
@GabrielLittman: ci sono già librerie consolidate scritte da sviluppatori esperti che eseguono confronti semver.
Dan Dascalescu,

82

semver

Il parser della versione semantica utilizzato da npm.

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

Linking versione semantica :
https://www.npmjs.com/package/semver#prerelease-identifiers


8
Sì. Questa è la risposta corretta - confrontare le versioni non è banale (vedi # 11 su semver.org ) e ci sono librerie a livello di produzione che fanno il lavoro.
Dan Dascalescu,

7
tecnicamente, non è la risposta giusta, poiché node.js e javascript sono diversi. Supponevo che la domanda originale fosse più mirata per il browser. Ma Google mi ha portato qui e per fortuna sto usando il nodo :)
Lee Gary,

2
NodeJS non è solo una soluzione solo lato server. Il framework di elettroni incorpora un nodeJS per applicazioni desktop. Questa è in realtà la risposta che stavo cercando.
Anthony Raymond,

2
semver è un pacchetto npm, può essere utilizzato su qualsiasi ambiente JS! QUESTA È LA
RISPOSTA GIUSTA

4
@artuska bene, quindi scegli semplicemente un altro pacchetto come semver-compare - 233B (meno di 0,5kB!) 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"));

Penso che la linea: var len = Math.min(a_components.length, b_components.length);farà sì che le versioni 2.0.1.1 e 2.0.1 siano trattate allo stesso modo?
Jon Egerton,

1
No. Guarda subito dopo il ciclo! Se una stringa è un prefisso dell'altra (ovvero il loop raggiunge la fine), quella più lunga viene considerata più alta.
Joe,

Forse sei stato scoraggiato dal mio inciampare sulla lingua inglese nel commento ...
Joe,

@Joe So che è una risposta un po 'vecchia ma stavo usando la funzione. Test a = '7'e b = '7.0'ritorni -1perché 7.0 è più lungo. Hai qualche suggerimento per quello? ( console.log(compare("7", "7.0")); //returns -1)
RaphaelDDL,

Suppongo che rientri nella rubrica del comportamento indefinito. Se disponi di questi numeri di versione, sono sicuro che puoi modificare la logica in base alle tue esigenze.
Joe,

48

Questa funzione di confronto molto piccola, ma molto veloce, prende numeri di versione di qualsiasi lunghezza e qualsiasi dimensione numerica per segmento .

Valori di ritorno:
- un numero < 0se a <b
- un numero > 0se a> b
-0 se a = b

Quindi puoi usarlo come funzione di confronto per Array.sort ();

EDIT: versione corretta di bug che rimuoveva gli zeri finali per riconoscere "1" e "1.0.0" come uguali

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


Errore con "0.0" e "0.0.0". Vedi violino: jsfiddle.net/emragins/9e9pweqg
emragins

1
@emragins Quando dovresti farlo?
Skylar Ittner,

1
@emragins: non vedo dove fallisce. Produce ["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"] dove i uscite di codice ["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"] , che è perfettamente lo stesso, dal 0,0 e 0.0.0 sono considerati uguali , il che significa che è irrilevante se '0,0' è prima '0.0.0' o viceversa.
LeJared

Sono d'accordo che questo è un punto normale. Sto usando questo con github.com/jonmiles/bootstrap-treeview , che suddivide i nodi in modo simile alle versioni, solo che in realtà sono solo nodi genitore / figlio e i loro indici. Ex. Genitore: 0,0, figlio: 0,0,0, 0,0,1. Vedi questo numero per maggiori dettagli sul perché mi interessa: github.com/jonmiles/bootstrap-treeview/issues/251
emragins

1
Vedi la risposta qui stackoverflow.com/questions/6611824/why-do-we-need-to-use-radix . I browser precedenti utilizzavano per indovinare il parametro radix se non specificato. Uno zero iniziale in una stringa numerica come la parte centrale in "1.09.12" veniva analizzato con radix = 8 risultante in numero 0 anziché numero atteso 9.
LeJared

14

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

Semplice, ma limitato a tre campi versione.
Dan Dascalescu,

11

Impossibile trovare una funzione che fa quello che volevo qui. Quindi ho scritto il mio. Questo è il mio contributo Spero che qualcuno lo trovi utile.

Professionisti:

  • Gestisce stringhe di versione di lunghezza arbitraria. '1' o '1.1.1.1.1'.

  • Ogni valore predefinito è 0 se non specificato. Solo perché una stringa è più lunga non significa che sia una versione più grande. ('1' dovrebbe essere uguale a '1.0' e '1.0.0.0'.)

  • Confronta i numeri non le stringhe. ('3' <'21' dovrebbe essere vero. Non falso.)

  • Non perdere tempo con confronti inutili nel ciclo. (Confronto per ==)

  • Puoi scegliere il tuo comparatore.

Contro:

  • Non gestisce le lettere nella stringa della versione. (Non so come avrebbe funzionato?)

Il mio codice, simile alla risposta accettata da 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);
}

Esempi :

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

questa versione è secondo me migliore di quella nella risposta approvata!
user3807877

1
Questa funzione è soggetta all'iniezione di codice se il parametro del comparatore viene utilizzato con input dell'utente non controllato! Esempio: compareVersions ('1.2', '== 0; alert ("cotcha");', '1.2');
LeJared,

@LeJared True. Quando l'ho scritto non lo usavamo con il codice inviato dall'utente. Avrebbe dovuto sollevarlo come un imbroglione probabilmente. Ora ho aggiornato il codice per eliminare quella possibilità. Ora però, quando il webpack e altri bundler node.js sono diventati prevalenti, suggerirei che la risposta di Mohammed Akdim sopra, usando semver, sarebbe quasi sempre la risposta corretta a questa domanda.
Viktor,

10

Funzione semplice e breve:

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
}

test:

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

Puoi semplificarlo con: const a = ~~ newParts [i]; In effetti questo è il modo più efficiente di convertire una stringa in un numero intero, che restituisce 0 se variabile non definita o contiene caratteri non numerici.
Vanowm,

5

Scusami se questa idea è già stata visitata in un link che non ho visto.

Ho avuto un certo successo con la conversione delle parti in una somma ponderata in questo modo:

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

Il che ha reso i confronti molto facili (confrontando un doppio). I nostri campi versione non sono mai più di 4 cifre.

7.10.2.184  -> 7010002184.0
7.11.0.1385 -> 7011001385.0

Spero che questo aiuti qualcuno, poiché i condizionali multipli sembrano un po 'eccessivi.


2
Questo si romperà, se this.minor> 999 (si sovrapporrà con major)
Afanasii Kurakin,

5

Ecco un'altra versione breve che funziona con qualsiasi numero di sotto-versioni, zeri imbottiti e numeri pari con lettere (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;
}

Produzione:

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

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

Codice più semplice per i browser moderni:

 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'idea qui è di confrontare i numeri ma sotto forma di stringa. per far funzionare il confronto, le due stringhe devono avere la stessa lunghezza. così:

"123" > "99" diventare "123" > "099"
riempimento del breve numero "aggiusta" il confronto

Qui riempio ogni parte con zeri a lunghezze di 10. Quindi uso semplicemente il confronto di stringhe per la risposta

Esempio :

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

spiegheresti funzione compareVersion2cosa succede esattamente?
Usman Wali,

Bene, allora puoi usare substringinvece che padStartper una migliore compatibilità, cioè var zeros = "0000000000"; '0.2.32'.split('.').map( s => zeros.substring(0, zeros.length-s.length) + s ).join('.') ti darà 0000000000.0000000002.0000000032:)
Usman Wali,


4

La mia risposta meno dettagliata della maggior parte delle risposte qui

/**
 * 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
dovresti renderlo un modulo e metterlo su node.js. fino ad allora, sto rubando il tuo codice con l'attribuzione a te. grazie per questo.
rww

3

Sebbene questa domanda abbia già molto risposte, ognuna promuove la propria soluzione prodotta nel cortile di casa, mentre per questo abbiamo un intero ecosistema di librerie (battaglie) testate.

Una rapida ricerca su NPM , GitHub , X ci fornirà alcune fantastiche librerie e vorrei esaminarne alcune:

semver-compareè un grande peso leggero (~ 230B) lib che è particolarmente utile se si desidera ordinare i numeri di versione, come metodo restituisce a vista della biblioteca -1, 0o 1in modo appropriato.

Il nucleo della 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 è piuttosto pesante (~ 4.4kB gzip), ma consente alcuni raffinati confronti unici come trovare il minimo / massimo di uno stack di versioni o scoprire se la versione fornita è unica o meno di qualsiasi altra cosa in una raccolta di versioni.

compare-versionsè un'altra piccola lib (~ 630B gzip) e segue bene le specifiche, il che significa che puoi confrontare le versioni con i flag alpha / beta e anche i caratteri jolly (come per le versioni minori / patch: 1.0.xo 1.0.*)

Il punto è: non è sempre necessario copiare e incollare il codice da StackOverflow, se riesci a trovare versioni decenti, (unit-) testate tramite il tuo gestore pacchetti preferito.


3

Ho affrontato il problema simile e avevo già creato una soluzione per questo. Sentiti libero di provarlo.

Restituisce 0per equal, 1se la versione è greatere -1se lo è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

L'idea è di confrontare due versioni e sapere qual è la più grande. Eliminiamo "." e confrontiamo ogni posizione del vettore con l'altra.

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

Risposta epica, esattamente quello che stavo cercando.
Vince,

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
Benvenuti in SO. Questa domanda ha già molte risposte valide, si prega di astenersi dall'aggiungere nuove risposte a meno che non si aggiunga qualcosa di nuovo.
ext

1

La replace()funzione sostituisce solo la prima occorrenza nella stringa. Quindi, sostituiamo il .con ,. Successivamente elimina tutto .e fai ,di .nuovo il to e analizzalo in float.

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

infine, ordinalo:

versions.sort();

1

Dai un'occhiata a questo post sul blog . Questa funzione funziona con numeri di versione numerici.

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

Se, ad esempio, vogliamo verificare se l'attuale versione di jQuery è inferiore a 1.8, si parseFloat($.ui.version) < 1.8 )otterrebbe un risultato errato se la versione è "1.10.1", poiché restituisce parseFloat ("1.10.1") 1.1. Anche un confronto tra stringhe potrebbe andare storto, poiché "1.8" < "1.10"valuta false.

Quindi abbiamo bisogno di un test come questo

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

La seguente funzione gestisce questo correttamente:

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

Ecco alcuni esempi:

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

Vedi qui per un esempio live e una suite di test: http://jsfiddle.net/mar10/8KjvP/


arghh, ho appena notato che ripper234 aveva pubblicato un URL di violino su alcuni commenti alcuni mesi fa che è abbastanza simile. Comunque, mantengo la mia risposta qui ...
mar10

Anche questo fallirà (come la maggior parte delle varianti) in questi casi: versionCompare ('1.09', '1.1') restituisce "1", allo stesso modo di versionCompare ('1.702', '1.8').
shaman.sir,

Il codice valuta "1.09"> "1.1" e "1.702"> "1.8", che ritengo sia corretto. Se non sei d'accordo: puoi indicare qualche risorsa a sostegno della tua opinione?
mar10

Dipende dai tuoi principi - come so non esiste una regola rigida o qualcosa del genere. Per quanto riguarda le risorse, l'articolo di Wikipedia per "Versioni del software" in "Sequenze incrementali" afferma che 1.81 potrebbe essere una versione minore di 1.8, quindi 1.8 dovrebbe essere letto come 1.80. L'articolo di versione semantica semver.org/spec/v2.0.0.html dice anche che 1.9.0 -> 1.10.0 -> 1.11.0, quindi 1.9.0 è trattato come 1.90.0 rispetto a questo. Quindi, seguendo questa logica, la versione 1.702 era precedente alla versione 1.8, che è trattata come 1.800.
shaman.sir,

1
Vedo che alcune regole trattano 1.8 <1.81 <1.9. Ma in semver useresti 1.8.1 invece di 1.81. Semver (a quanto ho capito) è definito attorno al presupposto che l'incremento di una parte genererà sempre una versione "successiva", quindi 1,8 <1,8,1 <1,9 <1,10 <1,81 <1,90 <1,100. Non vedo un'indicazione che questo sia limitato a due cifre. Quindi direi che il mio codice è pienamente compatibile con semver.
mar10

1

Ecco un'implementazione di coffeescript adatta per l'uso con Array.sort ispirata ad altre risposte qui:

# 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


questo non funziona correttamente .. ecco il risultato .. risultato ['1.1.1', '2.1.1', '3.3.1.0', '
3.1.1.0

1

Ho scritto un modulo nodo per ordinare le versioni, lo puoi trovare qui: version-sort

Caratteristiche :

  • nessun limite di sequenze "1.0.1.5.53.54654.114.1.154.45" funziona
  • nessun limite di lunghezza della sequenza: "1.1546515465451654654654654138754431574364321353734" funziona
  • può ordinare gli oggetti per versione (vedi README)
  • fasi (come alfa, beta, rc1, rc2)

Non esitare ad aprire un problema se hai bisogno di un'altra funzione.


1

Funziona con versioni numeriche di qualsiasi lunghezza separate da un punto. Restituisce vero solo se myVersion è> = minimumVersion, supponendo che la versione 1 sia inferiore a 1.0, la versione 1.1 sia inferiore a 1.1.0 e così via. Dovrebbe essere abbastanza semplice aggiungere condizioni extra come accettare numeri (basta convertirli in una stringa) ed esadecimali o rendere dinamico il delimitatore (basta aggiungere un parametro delimitatore quindi sostituire il "." Con il parametro)

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

Ecco alcuni test:

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

In alternativa, ecco una versione ricorsiva

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

Trovo il modo più semplice per confrontarli, non sono sicuro che sia quello che vuoi. quando corro sotto il codice nella console, ha senso e usando il metodo sort (), potrei ottenere l'array ordinato delle stringhe delle versioni. si basa sull'ordine alfabetico.

"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
Non funziona bene per i numeri di versione a due cifre, ad es. 1.10.0.
Leukipp,

1

Puoi usarlo String#localeCompareconoptions

sensibilità

Quali differenze nelle stringhe dovrebbero portare a valori di risultato diversi da zero. I valori possibili sono:

  • "base": Solo le stringhe che differiscono nelle lettere di base si equivalgono. Esempi: a ≠ b, a = á, a = A.
  • "accent": Solo le stringhe che differiscono per lettere o accenti di base e altri segni diacritici possono essere paragonate alla disparità. Esempi: a ≠ b, a ≠ á, a = A.
  • "case": Solo le stringhe che si differenziano per lettere di base o maiuscole si equivalgono. Esempi: a ≠ b, a = á, a ≠ A.
  • "variant": Stringhe che differiscono per lettere di base, accenti e altri segni diacritici o che il caso si confronta come disuguale. Altre differenze possono anche essere prese in considerazione. Esempi: a ≠ b, a ≠ á, a ≠ A.

L'impostazione predefinita è "variante" per l'utilizzo "ordina"; dipende dalle impostazioni locali per l'uso "cerca".

numerico

Se utilizzare le regole di confronto numeriche, in modo che "1" <"2" <"10". I valori possibili sono truee false; il valore predefinito è false. Questa opzione può essere impostata tramite una proprietà options o tramite una chiave di estensione Unicode; se vengono forniti entrambi, la optionsproprietà ha la precedenza. Le implementazioni non sono necessarie per supportare questa proprietà.

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


Come funziona davvero? Cos'è la lingua undefinedsopra? Come mai riesci a postare questo mentre leggo gli altri;)
mplungjan il

undefinedè la parte locale, non viene utilizzata qui.
Nina Scholz,

0

non potresti convertirli in numeri e poi ordinare in base alle dimensioni? Aggiungi 0 a quelli ai numeri che sono <4 di lunghezza

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

più grande è la versione, il numero più grande. Modifica: probabilmente deve essere modificato per tenere conto delle serie di versioni più grandi


Questo è solo un esempio, dato che deve fare alcune cose da solo: P Invece di 4, ottieni la quantità di numeri che ha la versione più grande, quindi riempi quelli più bassi di quelli con 0
Contra

0

Questo è un trucco pulito. Se hai a che fare con valori numerici, tra un intervallo specifico di valori, puoi assegnare un valore a ciascun livello dell'oggetto versione. Ad esempio "largeValue" è impostato su 0xFF qui, il che crea un aspetto molto "IP" per il tuo controllo delle versioni.

Questo gestisce anche il versioning alfanumerico (cioè 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

Mi piace la versione di @ mar10 , anche se dal mio punto di vista c'è una possibilità di uso improprio (sembra che non sia il caso se le versioni sono compatibili con il documento Semantic Versioning , ma potrebbe essere il caso se si utilizza un "numero di build" ):

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

Il problema qui è che i sotto-numeri del numero di versione sono, in alcuni casi, scritti con zero finali (almeno come lo vedo di recente mentre uso software diverso), che è simile alla parte razionale di un numero, quindi:

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

Il sotto-numero della prima (o della prima e della seconda) versione, tuttavia, viene sempre trattato come un valore intero a cui effettivamente corrisponde.

Se si utilizza questo tipo di controllo delle versioni, è possibile modificare solo alcune righe nell'esempio:

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

Quindi ogni sotto-numero tranne il primo verrà confrontato come float, così 09e 1diventerà 0.09e di 0.1conseguenza e confrontato correttamente in questo modo. 2054e 3diventerà 0.2054e 0.3.

La versione completa quindi è (credits to @ 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 È più lento, ma è anche possibile pensare a riutilizzare la stessa funzione di confronto operando il fatto che la stringa è in realtà l'array di caratteri:

 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

L'ho realizzato sulla base dell'idea di Kons e l'ho ottimizzato per la versione Java "1.7.0_45". È solo una funzione pensata per convertire una stringa di versione in un float. Questa è la funzione:

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 stringa "1.7.0_45" viene convertita in 1.0070000450000001 e questo è abbastanza buono per un confronto normale. Errore spiegato qui: come gestire la precisione del numero in virgola mobile in JavaScript? . Se sono necessarie più di 3 cifre su qualsiasi parte, è possibile modificare il divisoreMath.pow(10, i * 3); .

L'output sarà simile al seguente:

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

0

Ho avuto lo stesso problema del confronto delle versioni, ma con versioni che probabilmente contenevano qualcosa (es .: separatori che non erano punti, estensioni come rc1, rc2 ...).

Ho usato questo, che fondamentalmente ha diviso le stringhe di versione in numeri e non numeri, e cerca di confrontare di conseguenza con il tipo.

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

Ci sono alcune ipotesi qui per alcuni casi, ad esempio: "1.01" === "1.1" o "1.8" <"1.71". Non riesce a gestire "1.0.0-rc.1" <"1.0.0", come specificato da Semantic versionning 2.0.0


0

La preelaborazione delle versioni precedenti all'ordinamento significa che parseInt non viene chiamato più volte inutilmente. Utilizzando la mappa Array # simile al suggerimento di Michael Deal, ecco un tipo che uso per trovare la versione più recente di un semver standard in 3 parti:

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"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.