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


164

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

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

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


5
これは、fizzbuzzタイプのインタビューの質問として適しています。
スティーブクラリッジ

2
このため、すべてのソフトウェアバージョン番号は2001403のような整数である必要があります。「2.0.14.3」のようにわかりやすい方法で表示したい場合は、プレゼンテーション時にバージョン番号をフォーマットします。
jarmod 2013年

2
ここでの一般的な問題はセマンティックバージョンの比較であり、それは自明ではありませんsemver.orgの#11を参照)。幸いなことに、そのための公式ライブラリ、npmのセマンティックバージョナがあります。
Dan Dascalescu 2015

1
サーバーを比較する単純なスクリプトを見つけた
vsync

回答:


133

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

覚えておくべき重要な詳細がいくつかあります。

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

直接使用できる実装のコードを次に示します(要旨とドキュメント)。

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があります。これはripper234の作業を少し拡張したバージョンです(ありがとうございます)。

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


16
これはいくつかの単体テストを含む改善されたバージョンです:jsfiddle.net/ripper234/Xv9WL/28
ripper234

5
こんにちは、私はこの要点をテストとすべてを備えたギトレポにまとめ、npmとbowerに配置して、プロジェクトに簡単に含めることができるようにしました。 github.com/gabe0x02/version_compare
Gabriel Littman

2
@GabrielLittman:ねえ、時間を割いてくれてありがとう!ただし、SOのすべてのコードには、デフォルトでCC-BY-SAのライセンスが付与されています。つまり、パッケージをGPLライセンスにすることはできません。弁護士は誰のためにもここにいるわけではありませんが、それを修正すればい​​いでしょう。
Jon

2
@GabrielLittman:GPLは、既存のGPLコードと接触するすべてのコードをGPLライセンスに強制するという意味で、実際には非常に制限的です。とにかく、将来の参考のために:MITは、広く使用されている「文字列を添付せずに、何でもやりたいことは何でもできる」ライセンスです。
2014年

3
@GabrielLittman:いくつかの比較を実行するベテラン開発者によって作成されたライブラリがすでに確立されています
Dan Dascalescu、2015

82

半ば

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


8
はい。これが正解です。バージョンの比較は簡単ではなく(semver.orgの#11を参照)、その仕事を行う本番レベルのライブラリがあります。
Dan Dascalescu

7
技術的には、node.jsとjavascriptが異なるため、これは正しい答えではありません。元の質問はブラウザを対象としたものだったと思います。しかし、グーグルは私をここに連れてきて、幸いにも私はノードを使用しています:)
Lee Gary

2
NodeJSはサーバー側のみのソリューションではありません。Electronフレームワークは、デスクトップアプリケーション用のnodeJSを埋め込みます。これは実際に私が探していた答えです。
アンソニーレイモンド

2
それがnpmパッケージであるなら、どんなJS環境でも使用できます!これが正解です
ネイカー2017年

4
@artuskaは、semver-compareのような別のパッケージを実行するだけです-233B(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"));

私は行を考えます:var len = Math.min(a_components.length, b_components.length);バージョン2.0.1.1と2.0.1は同じように扱われますか?
Jon Egerton

1
いいえ。ループの直後を見てください。1つの文字列が他の文字列のプレフィックスである場合(つまり、ループが最後に到達する場合)、長い文字列が高い文字列として扱われます。
Joe

おそらく、コメントで私の英語のつまずきを先送りにされたのでしょう...
Joe

@Joeは少し古い答えですが、関数を使用していました。7.0の方が長いため、テストa = '7'してb = '7.0'復帰します-1。何か提案はありますか?(console.log(compare("7", "7.0")); //returns -1
RaphaelDDL 2013

私はそれが未定義の振る舞いの見出しの下に来ると思います。これらのバージョン番号がある場合は、要件に合わせてロジックを変更できます。
Joe

48

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

戻り値:
-数< 0<Bであれば
-数> 0の場合> B
- 0= 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"]


「0.0」と「0.0.0」で失敗しました。fiddleを参照してください:jsfiddle.net/emragins/9e9pweqg
emragins

1
@emraginsいつそれを行う必要がありますか?
Skylar Ittner 2016

1
@emragins:どこで失敗するかわかりません。0.0と0.0.0は等しいと見なされるため["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"] 、コードが出力する場所["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"] は完全に同じです。つまり、「0.0」が「0.0.0」の前か、またはその逆かは関係ありません。
LeJared 2016

これはいつものことだと思う。私はこれをgithub.com/jonmiles/bootstrap-treeviewで使用しています。バージョンと同様の方法でノードを階層化しますが、実際には親/子ノードとそのインデックスのみです。例 親:0.0、子:0.0.0、0.0.1 私が気にする理由の詳細については、この問題を参照してください:github.com/jonmiles/bootstrap-treeview/issues/251
emragins

1
こちらの回答をご覧ください。stackoverflow.com/questions/6611824/why-do-we-need-to-use-radix。指定されていない場合、古いブラウザは基数パラメータを推測するために使用されていました。代わりに、期待数9の番号0をもたらす基数= 8で解析するために使用される「1.09.12」の中間部等番号列における先頭のゼロ
LeJared

14

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

シンプルですが、3つのバージョンフィールドに制限されています。
Dan Dascalescu、2015

11

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

長所:

  • 任意の長さのバージョン文字列を処理します。「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

私の意見では、このバージョンは承認された回答のバージョンよりも優れています!
user3807877 2015年

1
未確認のユーザー入力でコンパレータパラメータを使用すると、この関数はコードインジェクションを起こしやすくなります。例:compareVersions( '1.2'、 '== 0; alert( "cotcha");'、 '1.2');
LeJared 2016

@LeJared True。私が書いたときは、ユーザーが投稿したコードでそれを使うつもりはありませんでした。多分コンとしてそれを育てるべきだった。その可能性を排除するためにコードを更新しました。しかし今、webpackや他のnode.jsバンドルが普及している場合、semverを使用した上記のMohammed Akdimの回答が、ほとんどの場合、この質問に対する正しい回答になると思います。
Viktor

10

シンプルで短い機能:

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

あなたはそれを単純化することができます: const a = ~~ newParts [i]; 実際、これは文字列を整数に変換する最も効率的な方法です。変数が未定義であるか、数値以外の文字が含まれている場合は0を返します。
vanowm

5

このアイデアが私が見たことのないリンクですでに訪問されている場合はご容赦ください。

次のように、パーツを加重和に変換することで、ある程度の成功を収めました。

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

複数の条件文は少しやり過ぎに見えるので、これが誰かに役立つことを願っています。


2
this.minor> 999(メジャーと重複する)の場合、これは壊れます
Afanasii Kurakin 2017

5

これは、任意の数のサブバージョンで機能する別の短いバージョンであり、ゼロが埋め込まれ、偶数は文字(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;
}

出力:

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

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

最新のブラウザー用の最も単純なコード:

 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"となって"123" > "099"
「修正」を比較して短い数を水増し

ここでは、各部分をゼロで埋めて長さを10にします。次に、答えとして単純な文字列比較を使用します

例:

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

compareVersion2正確に何が起こるか機能を説明できますか?
Usman Wali

良いです。互換性を高めるためにsubstring代わりに使用できます。padStartつまり var zeros = "0000000000"; '0.2.32'.split('.').map( s => zeros.substring(0, zeros.length-s.length) + s ).join('.') 0000000000.0000000002.0000000032:)
Usman Wali '12


4

私のここでのほとんどの答えよりも簡潔な答え

/**
 * 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
あなたはそれをモジュールにしてnode.jsに置くべきです。それまでは、私はあなたに帰属するコードを盗んでいます。これありがとう。
r3wt

3

この質問にはすでに多くの回答ありますが、それぞれが独自の裏庭で作成されたソリューションを促進しますが、このための(戦闘)テスト済みライブラリのエコシステム全体があります。

NPMGitHub、X で簡単に検索すると、いくつかの素敵なライブラリーが得られます。いくつか試してみたいと思います。

semver-compareあなたは、ライブラリの公開されたメソッドが戻ると、バージョン番号でソートしたい場合に特に便利です偉大な軽量(〜230B)libにある-10または1適切では。

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(〜630Bは、gzip圧縮された)別の小さなlibですし、あなたもワイルドカードアルファ/ベータフラグでバージョンを比較してことができることを意味し、きれいに仕様を次の(:マイナー/パッチバージョンのよう1.0.x1.0.*

重要なのは、選択したパッケージマネージャーを介して適切な(ユニット)テスト済みバージョンを見つけることができる場合は、StackOverflowからコードをコピーして貼り付ける必要があるとは限りません。


3

私も同様の問題に直面し、その解決策をすでに作成していました。ぜひお試しください。

それは返す0ためにequal1バージョンがある場合greater-1それがある場合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

アイデアは、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;
      }
   }
}

正解、まさに私が探していたもの。
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
SOへようこそ。この質問にはすでに良い回答がたくさんあります。何か新しいものを追加しない限り、新しい回答を追加しないでください。
2017年

1

replace()関数は文字列の最初の発生を置き換えます。だから、置き換えることができます.,。その後、すべて.を削除して,toを.再度作成し、解析してフロートにします。

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

最後に、並べ替えます。

versions.sort();

1

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

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

例えば、我々は現在のjQueryのバージョンが1.8未満であるかどうかを確認したい、場合は、parseFloat($.ui.version) < 1.8 )与えるだろう間違ったバージョンがparseFloatは(「1.10.1」)が復帰するので、「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/


arghh、ripper234がフィドルURLを数か月前のコメントに投稿していることに気づいたところ、それはよく似ています。とにかく、私はここに私の答えを保管します...
mar10

これも(ほとんどのバリアントとして)次の場合に失敗します:versionCompare( '1.09'、 '1.1')は、versionCompare( '1.702'、 '1.8')と同じように "1"を返します。
shaman.sir 2013

コードは「1.09」>「1.1」および「1.702」>「1.8」を評価しますが、これは正しいと思います。同意しない場合:あなたの意見を裏付けるリソースを指摘できますか?
mar10

それはあなたの原則に依存します—私が知っているように、厳密なルールや何かはありません。リソースについては、「インクリメントシーケンス」の「ソフトウェアのバージョン管理」に関するWikipediaの記事で、1.81は1.8のマイナーバージョンである可能性があるため、1.8は1.80と読む必要があります。セマンティックバージョニングの記事semver.org/spec/v2.0.0.htmlでは、1.9.0-> 1.10.0-> 1.11.0とも記載されているため、1.9.0は​​このように比較すると1.90.0として扱われます。したがって、このロジックに従うと、バージョン1.702はバージョン1.8より前で、バージョン1.800として扱われていました。
shaman.sir 2013

1
いくつかのルールは1.8 <1.81 <1.9を扱います。しかし、semverでは、1.81ではなく1.8.1を使用します。Semver(私が理解しているように)は、パーツをインクリメントすると常に「後の」バージョンが生成されるという想定に基づいて定義されているため、1.8 <1.8.1 <1.9 <1.10 <1.81 <1.90 <1.100です。これが2桁に制限されているという表示もありません。したがって、私のコードはsemverに完全に準拠していると言えます。
2013年

1

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

これはLeJaredの回答に触発されています
Dan Dascalescu 2015

この..ここで正常に動作していない結果である。..結果[ '1.1.1'、 '2.1.1'、 '3.3.1.0'、 '3.1.1.0']
ertan2002

1

バージョンをソートするためのノードモジュールを作成しました。ここで見つけることができます:version-sort

特徴

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

他の機能が必要な場合は、問題をオープンすることをためらわないでください。


1

これは、ピリオドで区切られた任意の長さの数値バージョンで機能します。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);
}

1

私はそれらを比較する最も簡単な方法を見つけます、それがあなたが望むものであるかどうかわかりません。コンソールで以下のコードを実行すると、意味があり、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"]

3
2桁のバージョン番号(1.10.0など)ではうまく機能しません。
Leukipp '19 / 06/19

1

あなたは使うことができString#localeCompareoptions

感度

文字列のどの違いがゼロ以外の結果値につながる必要があります。可能な値は次のとおりです。

  • "base":異なると比較されるのは、ベース文字が異なる文字列のみです。例:a ≠ ba = áa = A
  • "accent":基本文字またはアクセントが異なる文字列と他の分音記号のみが不等として比較されます。例:a ≠ ba ≠ áa = A
  • "case":不一致として比較されるのは、ベース文字または大文字と小文字が異なる文字列のみです。例:a ≠ ba = áa ≠ A
  • "variant":ベース文字、アクセント、その他の分音記号、または大文字と小文字が異なる文字列は、等しくないと比較されます。他の違いも考慮に入れられるかもしれません。例:a ≠ ba ≠ áa ≠ A

使用法「sort」のデフォルトは「variant」です。使用法「検索」はロケールに依存します。

数値

"1" <"2" <"10"のような数値照合を使用するかどうか。可能な値はtrueおよびfalseです。デフォルトはfalseです。このオプションは、オプションプロパティまたはUnicode拡張キーを使用して設定できます。両方が指定されている場合、optionsプロパティが優先されます。このプロパティをサポートするための実装は必要ありません。

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


これは実際どのように機能しますか?undefined上記、言語は何ですか?私が他の人を読んでいるときに、どうしてこれを投稿できたのでしょう;)
mplungjan

undefinedロケール部分です。ここでは使用されません。
Nina Scholz

0

それらを数値に変換してからサイズ順にソートできませんでしたか?長さが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  )
});

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


Pの代わりに4その後、0のと比べて低いものを記入し、最大のバージョンが持っている数字の量を取得し、:彼はいくつかのこと、自分自身を行うには持っているとして、単なる例であったことを
コントラ

0

これはきちんとしたトリックです。特定の範囲の値の間で数値を処理する場合は、バージョンオブジェクトの各レベルに値を割り当てることができます。たとえば、 "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

@ mar10 のバージョンが好きですが、私の見解では、誤用の可能性があります(バージョンがセマンティックバージョニングドキュメントと互換性がある場合はそうではないようですが、「ビルド番号」が使用されている場合はそうかもしれません。 ):

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

最初のものを除くすべてのサブ数がフロートとして比較されるように、これ091なるであろう0.09し、0.1それに応じて、適切にこの方法で比較しました。2054そして、3なるだろう0.20540.3

次に、完全なバージョンは(@ 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それは遅いですが、文字列が実際に文字の配列であるという事実を操作する同じ比較関数を再利用することを考えることも可能です:

 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

Konsのアイデアに基づいてこれを作成し、Javaバージョン「1.7.0_45」用に最適化しました。これは、バージョン文字列を浮動小数点数に変換するための関数です。これは関数です:

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

私はバージョン比較の同じ問題を抱えていましたが、バージョンに何かが含まれている可能性があります(つまり、ドットではないセパレータ、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"の管理に失敗する


0

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

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"

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.