web-dev-qa-db-ja.com

jsを使用してソフトウェアのバージョン番号を比較する方法は? (数字のみ)

ソフトウェアバージョン番号は次のとおりです。

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

これをどのように比較できますか?正しい順序は次のとおりです。

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

アイデアは簡単です...:最初の数字を読んで、2番目の数字を読んでから、3番目の数字を読んでください...しかし、バージョン番号を浮動小数点数に変換することはできません。この:

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

そして、これは背後にあるアイデアが何であるかを見るとより明確です...しかし、それをコンピュータプログラムに変換する方法は??これをソートする方法について何かアイデアはありますか?ありがとうございました。

143
Tattat

この比較を行う基本的な考え方は、Array.splitを使用して入力文字列からパーツの配列を取得し、2つの配列からパーツのペアを比較することです。パーツが等しくない場合、どのバージョンが小さいかがわかります。

留意すべき重要な詳細がいくつかあります。

  1. 各ペアの部品をどのように比較する必要がありますか?質問は数値的に比較したいのですが、数字だけで構成されていないバージョン文字列(「1.0a」など)がある場合はどうでしょうか。
  2. 1つのバージョン文字列が他のバージョン文字列よりも多くの部分を持っている場合はどうなりますか?ほとんどの場合、「1.0」は「1.0.1」よりも小さいと見なされますが、「1.0.0」はどうですか?

直接使用できる実装のコードは次のとおりです( Gist with 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;
}

このバージョンは、パーツを比較します 自然に 、文字の接尾辞を受け入れず、「1.7」が「1.7.0」より小さいと見なします。比較モードは辞書式に変更でき、オプションの3番目の引数を使用して、短いバージョンの文字列を自動的にゼロで埋めることができます。

「単体テスト」を実行するJSFiddleがあります here ; ripper234の作業 (ありがとう)のわずかに拡張されたバージョンです。

重要な注意:このコードは Array.mapArray.every を使用します。つまり、IEで実行されません。 _ 9よりも前のバージョン。これらをサポートする必要がある場合は、欠落しているメソッドにポリフィルを提供する必要があります。

118
Jon

semver

Npmが使用するセマンティックバージョンパーサー。

$ npm install semver

var semver = require('semver');

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

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

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

セマンティックバージョニングリンク
https://www.npmjs.com/package/semver#prerelease-identifiers

60
Mohammed Akdim
// 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"));
47
Joe

この非常に小さいが、非常に高速な比較関数は、任意の長さのバージョン番号およびセグメントごとの任意の数値サイズを取ります。

戻り値:
-数値< 0 if a <b
-番号> 0 if a> b
-0 if a = b

したがって、Array.sort();の関数を比較として使用できます

編集:「1」と「1.0.0」を等しいものとして認識するために末尾のゼロを取り除くバグ修正バージョン

function cmpVersions (a, b) {
    var i, diff;
    var regExStrip0 = /(\.0+)+$/;
    var segmentsA = a.replace(regExStrip0, '').split('.');
    var segmentsB = b.replace(regExStrip0, '').split('.');
    var l = Math.min(segmentsA.length, segmentsB.length);

    for (i = 0; i < l; i++) {
        diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
        if (diff) {
            return diff;
        }
    }
    return segmentsA.length - segmentsB.length;
}

// TEST
console.log(
['2.5.10.4159',
 '1.0.0',
 '0.5',
 '0.4.1',
 '1',
 '1.1',
 '0.0.0',
 '2.5.0',
 '2',
 '0.0',
 '2.5.10',
 '10.5',
 '1.25.4',
 '1.2.15'].sort(cmpVersions));
// Result:
// ["0.0.0", "0.0", "0.4.1", "0.5", "1.0.0", "1", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"]
42
LeJared

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

ここで望んでいたことをしている関数が見つかりませんでした。だから私は自分で書きました。これは私の貢献です。誰かがそれを役に立つと思うことを願っています。

長所:

  • 任意の長さのバージョン文字列を処理します。 「1」または「1.1.1.1.1」。

  • 指定しない場合、各値はデフォルトで0になります。文字列が長いからといって、それがより大きなバージョンであることを意味するわけではありません。 (「1」は「1.0」および「1.0.0.0」と同じでなければなりません。)

  • 文字列ではなく数値を比較します。 ( '3' <'21'はtrueである必要があります。falseではありません。)

  • ループ内の無駄な比較に時間を無駄にしないでください。 (==の比較)

  • 独自のコンパレータを選択できます。

短所:

  • バージョン文字列の文字は処理しません。 (それがどのように機能するかわかりませんか?)

マイコード、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);
}

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
8
Viktor

シンプルで短い機能:

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

テスト:

isNewerVersion('1.0', '2.0') // true
isNewerVersion('1.0', '1.0.1') // true
isNewerVersion('1.0.1', '1.0.10') // true
isNewerVersion('1.0.1', '1.0.1') // false
isNewerVersion('2.0', '1.0') // false
isNewerVersion('2', '1.0') // false
isNewerVersion('2.0.0.0.0.1', '2.1') // true
isNewerVersion('2.0.0.0.0.1', '2.0') // false
5
Arthur Araújo

私が見たことのないリンクでこのアイデアがすでに訪れているなら、私を許してください。

部品を次のように加重和に変換することに成功しました。

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

これにより、比較が非常に簡単になりました(二重の比較)。バージョンフィールドが4桁を超えることはありません。

7.10.2.184  -> 7010002184.0
7.11.0.1385 -> 7011001385.0

複数の条件が少し過剰に見えるので、これが誰かの助けになることを願っています。

4
Noxin

関数を確認してください version_compare() php.jsプロジェクトからPHPのversion_compare() に似ています。

次のように単純に使用できます。

version_compare('2.0', '2.0.0.1', '<'); 
// returns true
4
powtac

これは、任意の数のサブバージョン、ゼロが埋め込まれた偶数文字、および数字(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;
}

出力:

: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
/*use strict*/

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

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

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

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

3
vanowm

2017年の回答:

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

compareVersion(v1,v2) // return true

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

IE(padStartのレイク)をサポートしていないブラウザーのよりクールな答えは次のとおりです。

 function compareVersion2(ver1, ver2) {
      ver1 = ver1.split('.').map( s => s.padStart(10) ).join('.');
      ver2 = ver2.split('.').map( s => s.padStart(10) ).join('.');
      return ver1 <= ver2;
 }

ここでの考え方は、数字を比較することですが、文字列の形式です。比較を正しく簡単に行うには、2つの数字/文字列が同じ長さでなければなりません

例えば:

"123" > "99" // return false, but it wrong "123" "> "099" // but padding the short number with zeros make the comparision to be right

したがって、このソリューションのアイデアは、バージョンのすべての部分にゼロをパディングし、すべての数値が同じ幅10になるようにしてから、すべての文字列を結合することです。そのため、文字列全体を1回比較すると、正しい答えが得られます。

例えば ​​:

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
3
pery mimon
// 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
    }
2
Dyllan M

これは、ピリオドで区切られた任意の長さの数値バージョンで機能します。これは、myVersionが> = minimumVersionの場合にのみtrueを返し、バージョン1が1.0未満、バージョン1.1が1.1.0未満などと仮定します。数字の受け入れ(文字列への変換のみ)や16進数などの特別な条件を追加するか、区切り文字を動的にする(区切り文字パラメーターを追加してから "。"をparamに置き換える)のは簡単です。

function versionCompare(myVersion, minimumVersion) {

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

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

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

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

以下にいくつかのテストを示します。

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

または、ここに再帰バージョンがあります

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

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

ここに他の答えに触発されたArray.sortでの使用に適したcoffeescript実装があります:

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

バージョンをソートするためのノードモジュールを作成しました。ここにあります: version-sort

機能

  • シーケンス「1.0.1.5.53.54654.114.1.154.45」の制限は機能しません
  • シーケンスの長さに制限はありません: '1.1546515465451654654654654138754431574364321353734'は動作します
  • バージョン別にオブジェクトをソートできます(READMEを参照)
  • ステージ(アルファ、ベータ、rc1、rc2など)

他の機能が必要な場合は、問題を開くことをheしないでください。

1

replace()関数は、文字列の最初の出現のみを置き換えます。したがって、.,に置き換えましょう。その後、すべての.を削除し、,.に再度作成し、解析してフロートにします。

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

最後に、並べ替えます:

versions.sort();
1
Sascha Galley

たとえば、現在のjQueryバージョンが1.8未満であるかどうかを確認する場合、parseFloat($.ui.version) < 1.8 )は、バージョンが "1.10.1"の場合、wrong結果を返します。 parseFloat( "1.10.1")は1.1を返すため。 "1.8" < "1.10"falseに評価されるため、文字列の比較も失敗します。

したがって、このようなテストが必要です

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

次の関数はこれを正しく処理します。

/** Compare two dotted version strings (like '10.2.3').
 * @returns {Integer} 0: v1 == v2, -1: v1 < v2, 1: v1 > v2
 */
function versionCompare(v1, v2) {
    var v1parts = ("" + v1).split("."),
        v2parts = ("" + v2).split("."),
        minLength = Math.min(v1parts.length, v2parts.length),
        p1, p2, i;
    // Compare Tuple pair-by-pair. 
    for(i = 0; i < minLength; i++) {
        // Convert to integer if possible, because "8" > "10".
        p1 = parseInt(v1parts[i], 10);
        p2 = parseInt(v2parts[i], 10);
        if (isNaN(p1)){ p1 = v1parts[i]; } 
        if (isNaN(p2)){ p2 = v2parts[i]; } 
        if (p1 == p2) {
            continue;
        }else if (p1 > p2) {
            return 1;
        }else if (p1 < p2) {
            return -1;
        }
        // one operand is NaN
        return NaN;
    }
    // The longer Tuple is always considered 'greater'
    if (v1parts.length === v2parts.length) {
        return 0;
    }
    return (v1parts.length < v2parts.length) ? -1 : 1;
}

ここではいくつかの例を示します。

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

ライブサンプルおよびテストスイートについては、こちらをご覧ください。 http://jsfiddle.net/mar10/8KjvP/

1
mar10

それらを比較する最も簡単な方法を見つけましたが、それがあなたが望むものかどうかはわかりません。コンソールでコードの下で実行すると、理にかなっており、sort()メソッドを使用して、バージョン文字列のソートされた配列を取得できました。それはアルファベット順に基づいています。

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

ここでの回答のほとんどよりも冗長な回答

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

これを確認してください ブログ投稿 。この関数は、数値のバージョン番号に対して機能します。

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
David

アイデアは2つのバージョンを比較し、どちらが最大かを把握することです。 「。」を削除しますそして、ベクトルの各位置を他と比較します。

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

function compareVersions(a_components, b_components) {

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

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

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

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

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

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

再帰的アルゴリズムでそれを行う別の方法があります。

このコードはArray.shiftと再帰のみを使用します。つまり、IE 6+で実行できます。疑問がある場合は、 my GitHub にアクセスしてください。

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

まあ、このコードが誰かを助けることを願っています。

これがテストです。

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

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

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

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

これが私の解決策です。 leetcodeで受け入れました。今日のインタビューで問題に出会いました。しかし、私はその時にそれを解決しませんでした。もう一度考えました。ゼロを追加して、2つの配列の長さを等しくします。次に比較。

var compareVersion = function(version1, version2) {
    let arr1 = version1.split('.').map(Number);
    let arr2 = version2.split('.').map(Number);
    let diff = 0;
    if (arr1.length > arr2.length){
        diff = arr1.length - arr2.length;
        while (diff > 0){
            arr2.Push(0);
            diff--;
        } 
    }
    else if (arr1.length < arr2.length){
        diff = arr2.length - arr1.length;
        while (diff > 0){
            arr1.Push(0);
            diff--;
        }
    }
   
    let i = 0;
    while (i < arr1.length){
        if (arr1[i] > arr2[i]){
           return 1;
       } else if (arr1[i] < arr2[i]){
           return -1;
       }
        i++;
    }
    return 0;
    
};
0
metrical1

この質問にはすでにlotの回答がありますが、それぞれが独自の裏庭で醸造されたソリューションを推進していますが、これに対して(戦い)テストされたライブラリのエコシステム全体があります。

NPMGitHub 、Xをすばやく検索すると、Xに素敵なライブラリが提供されます。

semver-compare は、ライブラリの公開メソッドが-10、または1を適切に返すため、バージョン番号でソートする場合に特に便利な優れた軽量(〜230B)ライブラリです。

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 はサイズがかなり大きい(〜4.4kB gzip圧縮)が、バージョンのスタックの最小/最大を見つける、または指定されたバージョンが一意かバージョンのコレクションで他の何よりも少ない。

compare-versions はもう1つの小さなライブラリ(〜630B gzip圧縮)で仕様に準拠しています。つまり、アルファ/ベータフラグとワイルドカード(マイナー/パッチバージョンの場合:1.0.xまたは1.0.*)とバージョンを比較できます

ポイント:パッケージマネージャーを介して適切な(unit-)testedバージョンを見つけることができる場合、StackOverflowからコードをコピーアンドペーストする必要は必ずしもありません選択の。

0
kano

Konsのアイデアに基づいて作成し、Javaバージョン "1.7.0_45"用に最適化しました。これは、バージョン文字列をfloatに変換するための単なる関数です。これは機能です:

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

文字列 "1.7.0_45"は1.0070000450000001に変換され、通常の比較にはこれで十分です。ここで説明するエラー: JavaScriptでの浮動小数点数の精度の扱い方 。いずれかの部分で3桁以上必要な場合は、分周器Math.pow(10, i * 3);を変更できます。

出力は次のようになります。

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

これはきちんとしたトリックです。特定の範囲の値の間で数値を扱う場合は、バージョンオブジェクトの各レベルに値を割り当てることができます。たとえば、ここでは「largestValue」が0xFFに設定されているため、バージョン管理に対して非常に「IP」な外観になります。

これは、英数字バージョン管理も処理します(つまり、1.2a <1.2b)

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

バージョン from @ mar1 が好きですが、私の観点からは誤用の可能性があります(バージョンに互換性がある場合はそうではないようです セマンティックバージョニング ドキュメント。ただし、「ビルド番号」が使用されている場合があります)。

versionCompare( '1.09', '1.1');  // returns 1, which is wrong:  1.09 < 1.1
versionCompare('1.702', '1.8');  // returns 1, which is wrong: 1.702 < 1.8

ここでの問題は、バージョン番号のサブ番号が、場合によっては、末尾のゼロが切り取られて書き込まれることです(少なくとも、異なるソフトウェアを使用しているときに最近見たように)。これは、数値の有理数部分に似ています。

5.17.2054 > 5.17.2
5.17.2 == 5.17.20 == 5.17.200 == ... 
5.17.2054 > 5.17.20
5.17.2054 > 5.17.200
5.17.2054 > 5.17.2000
5.17.2054 > 5.17.20000
5.17.2054 < 5.17.20001
5.17.2054 < 5.17.3
5.17.2054 < 5.17.30

ただし、最初の(または最初と2番目の)バージョンのサブ番号は、常に実際に等しい整数値として扱われます。

この種のバージョン管理を使用する場合、例の数行だけを変更できます。

// replace this:
p1 = parseInt(v1parts[i], 10);
p2 = parseInt(v2parts[i], 10);
// with this:
p1 = i/* > 0 */ ? parseFloat('0.' + v1parts[i], 10) : parseInt(v1parts[i], 10);
p2 = i/* > 0 */ ? parseFloat('0.' + v2parts[i], 10) : parseInt(v2parts[i], 10);

したがって、最初のサブ番号を除くすべてのサブ番号はフロートとして比較されるため、09および10.09および0.1になり、このように適切に比較されます。 205430.20540.3になります。

その場合、完全なバージョンは( @ mar1 へのクレジット):

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

追伸遅いですが、文字列が実際には文字の配列であるという事実を操作する同じ比較関数を再利用することも考えられます:

 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
shaman.sir

関数は、バージョンが等しい場合は-1を返し、最初のバージョンが最新の場合は0を返し、2番目のバージョンが最新の場合は1を返します。

let v1 = '12.0.1.0'
let v2 = '12.0.1'

let temp1 = v1.split('.');
let temp2 = v2.split('.');

console.log(compareVersion(temp1, temp2))


function compareVersion(version1, version2) {
    let flag = false;
    var compareResult;
    let maxLength = Math.max(version1.length, version2.length); 
    let minLength = Math.min(version1.length, version2.length);

    for (let i = 0; i < maxLength; ++i ) {
        let result = version1[i] - version2[i];
        if (result > 0) {
            flag = true;
            compareResult = 0;
            break;
        }
        else if (result < 0) {
            flag = true;
            compareResult = 1;
            break;
        }

        if (i === minLength) {
            if (version1.length > version1.length) {
                compareResult = version1[version1.length-1] > 0 ? '0' : '-1'
            }  else  {
                compareResult = version1[version2.length-1] > 0 ? '1' : '-1'
            }
            break;
        }
    }
    if (flag === false) {
        compareResult = -1;
    }
    return compareResult;
}
0
leox

これは、質問に対する完全な解決策ではありませんが、非常に似ています。

このソート関数は、 セマンティックバージョン 用であり、解決済みバージョンを処理するため、x*などのワイルドカードでは機能しません。

この正規表現が一致するバージョンで動作します:/\d+\.\d+\.\d+.*$/1.2.3-devのようなバージョンでも動作することを除いて、 この回答 と非常に似ています。他の回答と比較して、不要なチェックをいくつか削除しましたが、私のソリューションは他のチェックと組み合わせることができます。

semVerSort = function(v1, v2) {
  var v1Array = v1.split('.');
  var v2Array = v2.split('.');
  for (var i=0; i<v1Array.length; ++i) {
    var a = v1Array[i];
    var b = v2Array[i];
    var aInt = parseInt(a, 10);
    var bInt = parseInt(b, 10);
    if (aInt === bInt) {
      var aLex = a.substr((""+aInt).length);
      var bLex = b.substr((""+bInt).length);
      if (aLex === '' && bLex !== '') return 1;
      if (aLex !== '' && bLex === '') return -1;
      if (aLex !== '' && bLex !== '') return aLex > bLex ? 1 : -1;
      continue;
    } else if (aInt > bInt) {
      return 1;
    } else {
      return -1;
    }
  }
  return 0;
}

マージされたソリューションは次のとおりです。

function versionCompare(v1, v2, options) {
    var zeroExtend = options && options.zeroExtend,
        v1parts = v1.split('.'),
        v2parts = v2.split('.');

    if (zeroExtend) {
        while (v1parts.length < v2parts.length) v1parts.Push("0");
        while (v2parts.length < v1parts.length) v2parts.Push("0");
    }

    for (var i = 0; i < v1parts.length; ++i) {
        if (v2parts.length == i) {
            return 1;
        }
        var v1Int = parseInt(v1parts[i], 10);
        var v2Int = parseInt(v2parts[i], 10);
        if (v1Int == v2Int) {
            var v1Lex = v1parts[i].substr((""+v1Int).length);
            var v2Lex = v2parts[i].substr((""+v2Int).length);
            if (v1Lex === '' && v2Lex !== '') return 1;
            if (v1Lex !== '' && v2Lex === '') return -1;
            if (v1Lex !== '' && v2Lex !== '') return v1Lex > v2Lex ? 1 : -1;
            continue;
        }
        else if (v1Int > v2Int) {
            return 1;
        }
        else {
            return -1;
        }
    }

    if (v1parts.length != v2parts.length) {
        return -1;
    }

    return 0;
}
0
timaschew

これは、サブストリングまたは配列を割り当てずにバージョン文字列を順序付けするバージョンです。割り当てるオブジェクトが少ないため、GCが行う作業が少なくなります。

getVersionPartメソッドの再利用を可能にするために)1組の割り当てがありますが、パフォーマンスに非常に敏感な場合は、割り当てを完全に回避するためにそれを拡張できます。

const compareVersionStrings : (a: string, b: string) => number = (a, b) =>
{
    var ia = {s:a,i:0}, ib = {s:b,i:0};
    while (true)
    {
        var na = getVersionPart(ia), nb = getVersionPart(ib);

        if (na === null && nb === null)
            return 0;
        if (na === null)
            return -1;
        if (nb === null)
            return 1;
        if (na > nb)
            return 1;
        if (na < nb)
            return -1;
    }
};

const zeroCharCode = '0'.charCodeAt(0);

const getVersionPart = (a : {s:string, i:number}) =>
{
    if (a.i >= a.s.length)
        return null;

    var n = 0;
    while (a.i < a.s.length)
    {
        if (a.s[a.i] === '.')
        {
            a.i++;
            break;
        }

        n *= 10;
        n += a.s.charCodeAt(a.i) - zeroCharCode;
        a.i++;
    }
    return n;
}
0
Drew Noakes

バージョン比較の問題は同じでしたが、バージョンに何かが含まれている可能性があります(つまり、ドットではないセパレーター、rc1、rc2 ...などの拡張子)。

私はこれを使用しました。これは基本的にバージョン文字列を数字と数字以外に分割し、それに応じてタイプと比較しようとします。

function versionCompare(a,b) {
  av = a.match(/([0-9]+|[^0-9]+)/g)
  bv = b.match(/([0-9]+|[^0-9]+)/g)
  for (;;) {
    ia = av.shift();
    ib = bv.shift();
    if ( (typeof ia === 'undefined') && (typeof ib === 'undefined') ) { return 0; }
    if (typeof ia === 'undefined') { ia = '' }
    if (typeof ib === 'undefined') { ib = '' }

    ian = parseInt(ia);
    ibn = parseInt(ib);
    if ( isNaN(ian) || isNaN(ibn) ) {
      // non-numeric comparison
      if (ia < ib) { return -1;}
      if (ia > ib) { return 1;}
    } else {
      if (ian < ibn) { return -1;}
      if (ian > ibn) { return 1;}
    }
  }
}

いくつかの場合、ここにはいくつかの仮定があります。たとえば、「1.01」===「1.1」、または「1.8」<「1.71」です。 Semantic versionning 2.0.0で指定されているように、「1.0.0-rc.1」<「1.0.0」の管理に失敗します

0
Uriel

ソートの前にバージョンを前処理することは、parseIntが不必要に複数回呼び出されないことを意味します。 Michael Dealの提案に似たArray#mapを使用して、標準の3パートsemverの最新バージョンを見つけるために使用するソートを次に示します。

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

実装は短く、シンプルでありながら強力なので、共有する価値があると思います。数字の比較のみを使用することに注意してください。通常、version2がversion1より新しいかどうかを確認し、その場合はtrueを返します。 version1:1.1.1およびversion2:1.1.2があるとします。 2つのバージョンの各部分を通過して、次のようにそれぞれの部分を追加します。バージョン1では(1 + 0.1)、次に(1.1 + 0.01)、バージョン2では(1 + 0.1)、次に(1.1 + 0.02)。

function compareVersions(version1, version2) {

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

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

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

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

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

バージョンのリストから最新バージョンを探したい場合、これは役に立つかもしれません:

function findLatestVersion(versions) {

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

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

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

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

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

    var sums = [];

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

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

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

それらを数値に変換し、サイズの後に並べ替えることはできませんか?長さが4未満の数字の1に0を追加します

コンソールで遊んだ:

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

バージョンが大きいほど、数字も大きくなります。編集:おそらくより大きなバージョンシリーズを考慮して調整する必要があります

0
Contra