設定 javascript どのようにjsを使用してソフトウェアのバージョン番号を比較する? (数字のみ)




15 Answers

// 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"));
javascript 入門

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

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

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




この非常に小さいが、非常に高速の比較関数は、任意の長さの 任意の数および任意の数のサイズをセグメントごとに取る。

戻り値:
- a <bの場合は数値< 0
- a> bの場合は数値> 0
- a = bの場合は0

Array.sort()のcompare関数として使うことができます

編集:バグ修正バージョン "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"]




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

長所:

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

  • 指定しない場合、各値は0にデフォルト設定されます。 文字列が長かったからといって、それがより大きなバージョンであるとは限りません。 ( '1'は '1.0'および '1.0.0.0'と同じにする必要があります)。

  • 数字ではない文字列を比較します。 ( '3' <'21'は真でなければなりません。

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

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

短所:

  • バージョン文字列の文字は扱えません。 (私はそれがどのように動作するのかわかりません?)

私のコードは、 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



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

私は、部品を以下のような加重和に変換することでいくつかの成功を収めました:

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

私はこれが複数の条件文が少し不合理に見えるので、誰かを助けてくれることを願っています。




ここに他の答えに触発された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



replace()関数は、文字列内の最初の出現箇所のみを置き換えます。 だから、それを置き換えることができます., 。 その後、すべてを削除します. そして, 〜にする. 再び浮動小数点数を解析します。

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

最後に、ソートしてください:

versions.sort();



例えば、現在のjQueryのバージョンが1.8より小さいかどうかをチェックしたい場合、parseFloat( "1.10。")以降、バージョンが "1.10.1"の場合、 parseFloat($.ui.version) < 1.8 )誤った結果を返します。 1 ")は1.1返します。 "1.8" < "1.10"false評価されるため、文字列比較も間違ってい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/ : http://jsfiddle.net/mar10/8KjvP/




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

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



あなたはそれらを数に変換してサイズの後でソートできませんでしたか? 長さが4未満の数字に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  )
});

バージョンが大きいほど、番号が大きくなります。 編集:おそらくより大きなバージョンのシリーズのアカウントに調整する必要があります




私はからのバージョンが好きですが、私の見解からは、誤用の可能性があります(バージョンがsemver.org/spec/v2.0.0.htmlドキュメントと互換性があるとは思われませんが、いくつかの "ビルド番号"が使用されている場合もあります):

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

しかしながら、第1(または第1および第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);

したがって、最初のものを除くすべてのサブ番号は浮動小数点として比較されるので、 091はそれに応じて0.090.1になり、このように正しく比較されます。 2054および30.2054および0.3となる。

完全版は、(クレジットは ):

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

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

 function cmp_ver(arr1, arr2) {
     // fill the tail of the array with smaller length with zeroes, to make both array have the same length
     while (min_arr.length < max_arr.length) {
         min_arr[min_arr.lentgh] = '0';
     }
     // compare every element in arr1 with corresponding element from arr2, 
     // but pass them into the same function, so string '2054' will act as
     // ['2','0','5','4'] and string '19', in this case, will become ['1', '9', '0', '0']
     for (i: 0 -> max_length) {
         var res = cmp_ver(arr1[i], arr2[i]);
         if (res !== 0) return res;
     }
 }



私は同じバージョンの比較の問題を抱えていましたが、おそらく何かを含むバージョン(つまり、ドットではないセパレータ、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」など、いくつかの場合にはいくつかの仮定があります。セマンティックバージョン2.0.0で指定されているように、 "1.0.0-rc.1" <"1.0.0"を管理することはできません




もう一つの実装私はそれが短くてシンプルで強力なので、分かち合う価値があると信じています。数字比較のみを使用することに注意してください。一般に、version2がversion1よりも後であるかどうかをチェックし、そうであればtrueを返します。バージョン1:1.1.1とバージョン2:1.1.2があるとします。バージョン1の場合は(1 + 0.1)、バージョン2の場合は(1 + 0.1)、次にバージョン2の場合は(1.1 + 0.02)のように、2つのバージョンの各部分を処理します。

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



これは非常に解決策ではありません質問が尋ねられたが、それは非常に似ています。

このソート機能はセマンティックバージョンです。解決済みのバージョンを処理するので、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;
}



サブストリングや配列を割り当てずにバージョン文字列を注文するバージョンです。オブジェクトの割り当てが少なくて済むため、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;
}



これが私の解決策です。それは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;
    
};




Related