views:

223

answers:

9

I have a version number with 3 digits as a String,

var version = "1.2.3";

and would like to compare it to another version. To see if version is newer than otherversion,

var otherVersion = "1.2.4";

How would you do it?

A: 

With one of the comparison operators.

"1.2.3" > "1.2.4" //false

"1.2.3" < "1.2.4" //true

Paul Butcher
`"1.2.3" < "1.2.10" // false but should be true`
RoToRa
Your example has four digits. The question explicitly mentions a three digit string.
Paul Butcher
It's still well possible that four digits occur. A very elegant but not stable enough solution IMO.
Pekka
That's true, but it's just as likely, and just as erroneous, that one of the version strings could be `"foo.bar.wibble.wobble"`, `"044.0x99.6"` or `null`. Most of the more complicated solutions here are equally as unstable.
Paul Butcher
+8  A: 

Pseudo:

  • Split both on .
  • Compare parts sequentially: Major -> Minor -> Rev (if part exist for both versions).
  • If oV[n] > v[n]: oV is greatest.
  • Else: Compare next subpart.

(See @arhorns answer for a elegant implementation)

jensgram
Missing items: "if no further subpart exists, version are identical" and possible after the splitting "assert both versions have the same number of parts".
Joachim Sauer
+2  A: 

If indeed you only have a single digit in each part why not just use straight comparison?

>>> var version = "1.2.3"; var otherVersion = "1.2.4"; version < otherVersion
true

It seems also to work with abbreviated versions:

>>> '1.2' > '1.2.4'
false
>>> '1.3' > '1.2.4'
true
SilentGhost
Wouldn't you want the comparison to support more than a single digit version part and wouldn't you want it to be able to compare "1.2" against "1.2.4" ?
arnorhs
@ahorhs: *I have a version number with 3 digits as a String,* Nevertheless, `'1.2' > '1.2.3'` comparison works perfectly fine.
SilentGhost
+1  A: 
function VersionValue(var str)
{
var tmp = str.split('.');
return (tmp[0] * 100) + (tmp[1] * 10) + tmp[2];
}

if (VersionValue(version) > VersionValue(otherVersion))...

for example

Jonas B
This would actually result in the version 0.1.1 being less than 0.0.15. Your answer needs to be more accurate, I think.
arnorhs
true but my basic point is that he could just split it and do the calcs.. what algorithm he uses is up to him
Jonas B
+2  A: 

You may want to use the following implementation (based on jensgram's solution):

function isNewer(a, b) {
   var partsA = a.split('.');
   var partsB = b.split('.');
   var numParts = partsA.length > partsB.length ? partsA.length : partsB.length;
   var i;

   for (i = 0; i < numParts; i++) {
      if ((parseInt(partsB[i], 10) || 0) !== (parseInt(partsA[i], 10) || 0)) {
         return ((parseInt(partsB[i], 10) || 0) > (parseInt(partsA[i], 10) || 0));
      }
   }

   return false;
}

console.log(isNewer('1.2.3', '1.2.4'));    // true
console.log(isNewer('1.2.3', '1.2.0'));    // false
console.log(isNewer('1.2.3', '1.2.3.1'));  // true
console.log(isNewer('1.2.3', '1.2.2.9'));  // false
console.log(isNewer('1.2.3', '1.2.10'));   // true

Note that the use of parseInt() is necessary, because otherwise the last test would return false: "10" > "3" returns false.

Daniel Vassallo
`for each (i in aVersion)` ? For an array? Why?
npup
why error when the versions have different number of parts? Why not have it support all?
arnorhs
@arnorhs: There you go :) ... @npup: Fixed.
Daniel Vassallo
I've put your implementation in and it works very good :) thanks mate! Nice finding the bug with 10, hadnt thought about that
heffaklump
A: 

Maybe like this (quickie)?

function isNewer(s0, s1) {
    var v0 = s0.split('.'), v1 = s1.split('.');
    var len0 = v0.length, len1=v1.length;
    var temp0, temp1, idx = 0;
    while (idx<len0) {
        temp0 = parseInt(v0[idx], 10);
        if (len1>idx) {
            temp1 = parseInt(v1[idx], 10);
            if (temp1>temp0) {return true;}
        }
        idx += 1;
    }
    if (parseInt(v0[idx-1], 10)>parseInt(v1[idx-1], 10)) {return false;}
    return len1 > len0;
}
var version = "1.2.3";
var otherVersion = "1.2.4";

console.log('newer:'+(isNewer(version, otherVersion)));

It takes care of different number of parts, but it works only with numbers between the dots though.

npup
It seems awefully long, complicated and only supports versions with three version parts
arnorhs
Heh, it's not complicated. It's just a loop. It surely *DOES* support more than three parts (that's what makes it a bit longer than some other suggestions here). And IMO it's not much longer han it has to be, taking into consideration that it has to 1) compare each field till it finds out the second is "newer" (then we're done) and *else* 2) if second string has more fields, then it is also newer.
npup
temp1 and temp2 should be temp0 and temp1 for consistency. Or preferably something describing, like num0 and num1.
Christoffer Hammarström
Oh sure./the npup
npup
@npup: You may want to use `parseInt(x, 10)` because without specifying the base, `isNewer("1.2.03", "1.2.08")` returns `false`, while `isNewer("1.2.03", "1.2.07")` returns `true`.
Daniel Vassallo
Good idea, thanks! One never knows what stuff people feed you these days.
npup
+4  A: 

The problem with most of the submitted versions is they can't handle any number of version parts (eg. 1.4.2 .. 1.2 etc) and/or they have the requirement of the version part being a single digit, which is not that common actually.

Improved compareVersions() function

This function will return 1 if v1 is greater than v2, -1 if v2 is greater and 0 if the versions are equal (handy for custom sorting as well)

I'm not doing any error checking on the inputs.

function compareVersions (v1, v2)
{
    v1 = v1.split('.');
    v2 = v2.split('.');
    var longestLength = (v1.length > v2.length) ? v1.length : v2.length;
    for (var i = 0; i < longestLength; i++) {
        if (v1[i] != v2[i]) {
            return (v1 > v2) ? 1 : -1
        }
    }
    return 0; 
 }
arnorhs
Nice. `var longestLength = Math.max(v1.length, v2.length);`
jensgram
@arnorhs: Just noticed that you already made an attempt at this. However, `compareVersions('1.2.3', '1.2.3.0')` will return `-1`, but I guess it should return `0`. You may want to use the `||` operator to default an `undefined` to `0` in `(v1[i] != v2[i])`. In addition, you have a typo in `i < longestLenght`.
Daniel Vassallo
this worked perfectly. thanks! spelling error on longestLenght btw ;)
heffaklump
@arnorhs: In addition, `compareVersions('1.2.3', '1.2.10')` will also return `1` when it should return `-1`. You need `parseInt()` to fix this.
Daniel Vassallo
@heffaklump: It won't work perfectly in all situations, unless the issues I mentioned in the two comments above are addressed.
Daniel Vassallo
@daniel ooh. yea, thats true. hadnt tested it enough
heffaklump
+1  A: 

Since I'm bored, here's an approach similar to our decimal system (tens, hundreds, thousands, etc) which uses a regex callback instead of a loop:

function compareVersion(a, b) {
    var expr = /\d+/g, places = Math.max(a.split(expr).length, b.split(expr).length);
    function convert(s) {
        var place = Math.pow(100, places), total = 0;
        s.replace(expr,
            function (n) {
                total += n * place;
                place /= 100;
            }
        );
        return total;
    };

    if (convert(a) > convert(b)) {
        return a;
    }

    return b;
}

It returns the greater version, e.g.:

compareVersion('1.4', '1.3.99.32.60.4'); // => 1.4
Matt
A: 

Note, that none of these solutions will knowingly return the right result for things like 0.9beta or 1.0 RC 1. It is, however, handled quite intuitively in PHP: http://de3.php.net/manual/en/function.version-compare.php and there is a JS port of this: http://phpjs.org/functions/version_compare (I don't claim this to be very nice or efficient, just kind of 'complete').

Boldewyn