2011-10-10 13 views
9

Czy ktoś ma kod do porównania dwóch numerów wersji w JavaScript? Chcę tylko prostych porównań wersji (np. "1.0" vs "1.5.6") i powinno działać z liczbami lub ciągami. Może zignorować końcowe identyfikatory beta, takie jak "1.5.6b4", ale w przeciwnym razie może oczekiwać, że łańcuchy będą dobrze uformowane. Funkcja powinna zwracać liczbę całkowitą ze znakiem, taką jak normalna funkcja cmp.Jak mogę porównać dowolne numery wersji?

function cmpVersion(a, b) 
    return less than one if a < b 
    return 0 if a == b 
    return greater than one if a > b 

Mam odpowiedź, ale wybiorę lepsze lub bardziej eleganckie rozwiązanie na własną rękę.

(używam tego porównać jQuery.browser.version liczb, ale odpowiedź będzie bardziej powszechnie stosowane)

Odpowiedz

14
function cmpVersion(a, b) { 
    var i, cmp, len; 
    a = (a + '').split('.'); 
    b = (b + '').split('.'); 
    len = Math.max(a.length, b.length); 
    for(i = 0; i < len; i++) { 
     if(a[i] === undefined) { 
      a[i] = '0'; 
     } 
     if(b[i] === undefined) { 
      b[i] = '0'; 
     } 
     cmp = parseInt(a[i], 10) - parseInt(b[i], 10); 
     if(cmp !== 0) { 
      return (cmp < 0 ? -1 : 1); 
     } 
    } 
    return 0; 
} 

function gteVersion(a, b) { 
    return cmpVersion(a, b) >= 0; 
} 
function ltVersion(a, b) { 
    return cmpVersion(a, b) < 0; 
} 

Ta funkcja obsługuje:

  • cyfr lub napisów jako wejście
  • kończące zera (np cmpVersion("1.0", 1) powraca 0)
  • ignoruje spływu alpha, b, pre4 itp
+0

Wyrecytowanie przekształciłoby" 1.02 "na" 1 ", więc' cmpVersion ('1.01', '1.02') 'daje błędny wynik. –

+0

Edytowałem kod tej odpowiedzi za pomocą nowej metody, która rozwiązuje powyższy problem. Recenzje byłyby bardzo mile widziane, zwłaszcza w celu zauważenia jakichkolwiek regresji. Nawiasem mówiąc, ten kod nadal nie obsługuje 'cmpVersion ('1.2alpha4', '1.2alpha5')', ale myślę, że byłoby to bardziej skomplikowane do wdrożenia. –

0

Jeśli nie dbają o .5.6, należy parseInt

var majorV = parseInt("1.5.6",10) 

Ponieważ mówiłeś dbać o mniejszych wersjach:

function cmpVersion(v1, v2) { 
    if(v1===v2) return 0; 
    var a1 = v1.toString().split("."); 
    var a2 = v2.toString().split("."); 
    for(var i = 0; i < a1.length && i < a2.length; i++) { 
     var diff = parseInt(a1[i],10) - parseInt(a2[i],10); 
     if(diff>0) { 
      return 1; 
     } 
     else if(diff<0) { 
      return -1; 
     } 
    } 
    diff = a1.length - a2.length; 
    return (diff>0) ? 1 : (diff<0) ? -1 : 0; 
} 

console.log(cmpVersion("1.0", "1.56")); 
console.log(cmpVersion("1.56", "1.56")); 
console.log(cmpVersion("1.65", "1.5.6")); 
console.log(cmpVersion("1.0", "1.5.6b3")); 
+2

Zgodnie z tym podejściem, '1.9.5 == 1.0.1'. Zakładając, że OP nie dba o nic, ale główna wersja jest dość odważnym założeniem. – delnan

+0

Absolutnie dbam o '.5.6' – theazureshadow

4

Jeśli chcesz być w pełni poprawne, przyjrzeć się dyskusji na PEP386, szczególnie nagłówek „Nowy algorytm wersji”.

W przeciwnym razie wydaje się, że odpowiedź jest całkiem dobra.

+1

+1: "Numerowanie wersji dla anarchistów i programistów." Lubię to. Nie jestem pewien, czy chcę obsłużyć * wszystkie * skrajne przypadki, ale jest to świetne odniesienie. Dzięki! – theazureshadow

0
function compareVersion(a, b) { 
    return compareVersionRecursive(a.split("."), b.split(".")); 
} 

function compareVersionRecursive(a, b) { 
    if (a.length == 0) { 
     a = [0]; 
    } 
    if (b.length == 0) { 
     b = [0]; 
    } 
    if (a[0] != b[0] || (a.length == 1 && b.length == 1)) { 
     return a[0] - b[0]; 
    } 
    return compareVersionRecursive(a.slice(1), b.slice(1)); 
} 
2

npm wykorzystuje piękny składni porównać wersje i można dostać ten sam moduł tutaj: https://github.com/isaacs/node-semver

następujące style range są obsługiwane:

  • 1.2.3 konkretnej wersji. Kiedy nic innego nie zrobi. Zauważ, że metadane kompilacji są nadal ignorowane, więc 1.2.3+build2012 spełni ten zakres.
  • >1.2.3 Większa niż określona wersja.
  • <1.2.3 Mniej niż określona wersja. Jeśli nie ma wstępnego tagu w zakresie wersji, nie będzie również dozwolona żadna wersja wstępna, nawet jeśli są one technicznie "mniejsze niż".
  • >=1.2.3 Większy lub równy. Zauważ, że wersje przedpremierowe NIE są równe ich "normalnym" odpowiednikom, więc 1.2.3-beta nie spełni tego zakresu, ale będzie to robił 2.3.0-beta.
  • <=1.2.3 Mniejsza niż lub równa. W takim przypadku wersje wstępne są dozwolone, więc może spełnić wymagania 1.2.3-beta.
  • 1.2.3 - 2.3.4: = >=1.2.3 <=2.3.4
  • ~1.2.3: = >=1.2.3-0 <1.3.0-0 "Dość blisko do 1.2.3". Podczas korzystania z operatorów tyldy obsługiwane są również wersje w wersji wstępnej, ale wstępne wydanie następnej znaczącej cyfry NIE będzie zadowalające, więc nie będzie spełniać ~1.2.3.
  • ^1.2.3: = >=1.2.3-0 <2.0.0-0 "Kompatybilny z 1.2.3". Podczas korzystania z operatorów opiekunów, wszystko od określonej wersji (włącznie z wersją wstępną) będzie obsługiwane aż do następnej wersji głównej (lub jej wcześniejszych wersji), ale bez niej. 1.5.1 zadowoli ^1.2.3, natomiast 1.2.2 i 2.0.0-beta nie będzie.
  • ^0.1.3: = >=0.1.3-0 <0.2.0-0 "Kompatybilny z 0.1.3". Wersje 0.x.x są specjalne: pierwszy niezerowy komponent wskazuje potencjalnie przełamujące zmiany, co oznacza, że ​​operator-opiekun pasuje do dowolnej wersji z tym samym pierwszym niezerowym składnikiem, zaczynającym się od określonej wersji.
  • ^0.0.2: = =0.0.2 "Jedynie wersja 0.0.2 jest uważane za zgodne"
  • ~1.2: = >=1.2.0-0 <1.3.0-0 "Każda wersja wychodząc z 1,2"
  • ^1.2: = >=1.2.0-0 <2.0.0-0 "Każda wersja kompatybilna z 1.2"
  • 1.2.x : = >=1.2.0-0 <1.3.0-0 "Każda wersja wychodząc z 1,2"
  • ~1: = >=1.0.0-0 <2.0.0-0 "Każda wersja wychodząc z 1"
  • ^1: = >=1.0.0-0 <2.0.0-0 „Każda wersja kompatybilna z 1”
  • 1.x: = >=1.0.0-0 <2.0.0-0 „Każda wersja wychodząc z 1”

zakresy mogą być połączone zarówno z przestrzeni (co oznacza „i”) lub || (co oznacza "lub").

-1

Ta funkcja zwraca wartość true, jeśli wersja jest większa lub równa wersji minimalnej. Zakłada się, że 1.0 jest większe niż 1, gdy wersje są ciągami. Kiedy są liczbami, mówią, że są takie same. Jeśli chcesz, aby oba typy zwracały te same wartości, musisz przekonwertować liczby na łańcuchy, co również jest łatwe. lub możesz zmodyfikować warunek ciągu znaków, aby sprawdzić, czy dłuższy numer wersji ma wszystkie końcowe zera, takie jak 1.1 do 1.1.0.0.0.0. drugi to wszystkie zera końcowe:

function doesMyVersionMeetMinimum(myVersion, minimumVersion) { 

    if(typeof myVersion === 'number' && typeof minimumVersion === 'number') { 
     return(myVersion >= minimumVersion); 
    } 

    var v1 = myVersion.split("."), v2 = minimumVersion.split("."), minLength; 

    minLength= Math.min(v1.length, v2.length); 

    for(i=0; i<minLength; i++) { 
     if(Number(v1[i]) < Number(v2[i])) { 
      return false; 
     } 
     else if(Number(v1[i]) < Number(v2[i])) { 
      return true; 
     }   

    } 

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

Oddałem moją kopalnię tak, aby była tak zwięzła, jak tylko mogę. Nie ma sprawdzenia zer końcowych, ale działa dla liczb kompilacji dowolnej długości (na przykład major, major.minor, major.minor.build).

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

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

Zasadniczo najpierw utworzyłem nową tablicę z każdego ciągu wersji, aby móc porównać każdą cyfrę osobno. Następnie w pętli for wybieram długość najdłuższego ciągu wersji (lub długości pierwszej wersji, jeśli mają taką samą długość).

Instrukcja if sprawdza, czy w b znajduje się cyfra, ale nie w b, lub jeśli cyfra a jest większa niż cyfra b odpowiadającego symbolu zastępczego, w takim przypadku zwróci wartość "mniej niż jeden" .

Podobnie, instrukcja else sprawdza, czy istnieje cyfra wb, ale nie w a lub jeśli cyfra b jest większa od cyfry a dla odpowiadającej wartości miejsca, w którym to przypadku będzie zwracana " więcej niż jeden ".

Ostatnia instrukcja return 0 to catch-all, do której nasza funkcja dotrze, jeśli ciągi wersji są równe.

0

Mam spreparowane następującą funkcję, która wspiera końcowe liter początkowych zer ... (patrz przykłady poniżej):

function cmpVersions(a, b) { 

    var partsA = a.split('.'); 
    var partsB = b.split('.'); 
    var nbParts = Math.max(partsA.length, partsB.length); 

    for (var i = 0; i < nbParts; ++i) { 
     if (partsA[i] === undefined) { 
      partsA[i] = '0'; 
     } 
     if (partsB[i] === undefined) { 
      partsB[i] = '0'; 
     } 

     // edit: added this part 
     // - fixes the important case "1.2/1.10" 
     // - but breaks the not-so-important case "1.02/1.1" 
     var intA = parseInt(partsA[i], 10); 
     var intB = parseInt(partsB[i], 10); 
     if (!isNaN(intA) && !isNaN(intB)) { 
      if (intA > intB) { 
       return 1; 
      } else if (intA < intB) { 
       return -1; 
      } 
     } 

     var compare = partsA[i].localeCompare(partsB[i]); 
     if (compare !== 0) { 
      return compare; 
     } 
    } 

    return 0; 
} 

Tak, kilka przykładów:

// trailing letters 
cmpVersion('1.0a', '1.0b'); // -1 

// leading zeroes 
cmpVersion('1.01', '1.1'); // -1 

// "zero" parts 
cmpVersion('1', '1.0'); // 0 


Jeśli don Konieczne jest wsparcie wiodących zer, oto prostsza alternatywa:

function cmpVersions(a, b) { 

    function padParts(version) { 
     return version 
      .split('.') 
      .map(function (part) { 
       return '00000000'.substr(0, 8 - part.length) + part; 
      }) 
      .join('.'); 
    } 

    a = padParts(a); 
    b = padParts(b); 

    return a.localeCompare(b); 
} 


Szybka aktualizacja: Zauważyłem potem, że pierwsza funkcja sortuje "1.2" przed "1.10", co jest rażąco błędne. Ponadto "znaczące wiodące zera" są trudne i niejednoznaczne (zarówno do interpretacji, jak i do implementacji), a edycja semantyczna wyraźnie ich unika. Dlatego uważam, że druga funkcja powinna być zawsze preferowana.

Aktualizacja 2: Ale rodzaju druga funkcja „1.2a” przed „1.1” ... Myślę, że po prostu nie ma „jeden dla wszystkich” funkcji ... Pick „bardziej odpowiedni” działa zgodnie z Użyj przypadku, lub lepiej, sortuj według daty, jeśli możesz.

Aktualizacja 3: Zmodyfikowano pierwszą funkcję do prawidłowego obchodzenia się z ważnym przypadkiem "1.2/1.10". Efektem ubocznym jest przełamanie nieistotnego przypadku "1.02/1.1" i najwyraźniej jest to obecnie jedyne zastrzeżenie (może uda się je naprawić, ale nie jestem pewien, czy warto). Dlatego teraz polecam stałą, pierwszą funkcję.

Powiązane problemy