How to use Javascript math on a version number - javascript

I use jQuery to get the browser version like this:
var x = $.browser.version;
I get a string like this: 1.9.1.1
Now, I want to do an evaluation so if x is >= 1.9.1 then do some stuff. Unfortunately, with multiple decimal points, I cannot do a parseFloat() because it converts 1.9.1.1 to simply 1.9, and the if evaluation would match a 1.9.0 version (which I do not want).
Has someone figured out a way to accomplish turning a version number (with multiple decimals) into something that can be used as a number for evaluation (or some other way to accomplish what I am trying to do here)?
Thanks -

You could do something with string.split and then do a digit by digit comparison
// arr[0] = 1
// arr[1] = 9
// arr[2] = 1
// arr[3] = 1
var arr = ($.browser.version).split('.');
The following is taken from this post
This is a function that will parse your version string and give you back a JSON object
function parseVersionString (str) {
if (typeof(str) != 'string') { return false; }
var x = str.split('.');
// parse from string or default to 0 if can't parse
var maj = parseInt(x[0]) || 0;
var min = parseInt(x[1]) || 0;
var bld = parseInt(x[2]) || 0;
var rev = parseInt(x[3]) || 0;
return {
major: maj,
minor: min,
build: bld,
revision: rev
}
}
Then you could use the following syntax
var version = parseVersionString($.browser.version);
// version.major == 1
// version.minor == 9
// version.build == 1
// version.revision == 1

Here's another version of versionCmp():
function versionCmp(v1, v2) {
v1 = String(v1).split('.');
v2 = String(v2).split('.');
var diff = 0;
while((v1.length || v2.length) && !diff)
diff = (+v1.shift() || 0) - (+v2.shift() || 0);
return (diff > 0) - (diff < 0);
}
Another possibility would be to assign a numeric value to each version number:
function valueOfVersion(ver) {
ver = String(ver).split('.');
var value = 0;
for(var i = ver.length; i--;)
value += ver[i] / Math.pow(2, i * 8) || 0;
return value;
}
This only works if each digit is less than 256 (because of the hard-coded divisor) and has a limited precision (ie the version strings can't get arbitrarily long).

You need to treat each portion of the string as a seperate integer, so split and iterate, and cmp:
// perform cmp(a, b)
// -1 = a is smaller
// 0 = equal
// 1 = a is bigger
function versionCmp(a, b) {
a = a.split(".");
b = b.split(".");
for(var i=0; i < a.length; i++) {
av = parseInt(a[i]);
bv = parseInt(b[i]);
if (av < bv) {
return -1;
} else if (av > bv) {
return 1;
}
}
return 0;
}
console.log(versionCmp("1.1.2.3", "1.2.1.0")); // should be -1
console.log(versionCmp("1.19.0.1", "1.2.0.4")); // should be 1
console.log(versionCmp("1.2.3.4", "1.2.3.4")); // should be 0

You could remove all dots and then parse it as an integer.
Take note tho, this solution doesn't work in the long term.

Related

Floating point comparison with zero at end [duplicate]

Here is the software version number:
"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"
How can I compare this?
Assume the correct order is:
"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"
The idea is simple...:
Read the first digit, than, the second, after that the third...
But I can't convert the version number to float number...
You also can see the version number like this:
"1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1.0"
And this is clearer to see what is the idea behind...
But, how can I convert it into a computer program?
semver
The semantic version parser used by 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, '*')
Semantic Versioning Link : https://www.npmjs.com/package/semver#prerelease-identifiers
The basic idea to make this comparison would be to use Array.split to get arrays of parts from the input strings and then compare pairs of parts from the two arrays; if the parts are not equal we know which version is smaller.
There are a few of important details to keep in mind:
How should the parts in each pair be compared? The question wants to compare numerically, but what if we have version strings that are not made up of just digits (e.g. "1.0a")?
What should happen if one version string has more parts than the other? Most likely "1.0" should be considered less than "1.0.1", but what about "1.0.0"?
Here's the code for an implementation that you can use directly (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;
}
This version compares parts naturally, does not accept character suffixes and considers "1.7" to be smaller than "1.7.0". The comparison mode can be changed to lexicographical and shorter version strings can be automatically zero-padded using the optional third argument.
There is a JSFiddle that runs "unit tests" here; it is a slightly expanded version of ripper234's work (thank you).
Important note: This code uses Array.map and Array.every, which means that it will not run in IE versions earlier than 9. If you need to support those you will have to provide polyfills for the missing methods.
The simplest is to use localeCompare :
a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })
This will return:
0: version strings are equal
1: version a is greater than b
-1: version b is greater than a
// 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"));
This very small, yet very fast compare function takes version numbers of any length and any number size per segment.
Return values:
- a number < 0 if a < b
- a number > 0 if a > b
- 0 if a = b
So you can use it as compare function for Array.sort();
EDIT: Bugfixed Version stripping trailing zeros to recognize "1" and "1.0.0" as equal
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"]
Simple and short function:
function isNewerVersion (oldVer, newVer) {
const oldParts = oldVer.split('.')
const newParts = newVer.split('.')
for (var i = 0; i < newParts.length; i++) {
const a = ~~newParts[i] // parse int
const b = ~~oldParts[i] // parse int
if (a > b) return true
if (a < b) return false
}
return false
}
Tests:
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
Taken from 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;
}
Here is another short version that works with any number of sub versions, padded zeros and even numbers with letters (1.0.0b3)
const compareVer = ((prep, repl) =>
{
prep = t => ("" + t)
//treat non-numerical characters as lower version
//replacing them with a negative number based on charcode of first character
.replace(/[^0-9\.]+/g, c => "." + (c.replace(/[\W_]+/, "").toLowerCase().charCodeAt(0) - 65536) + ".")
//remove trailing "." and "0" if followed by non-numerical characters (1.0.0b);
.replace(/(?:\.0+)*(\.-[0-9]+)(\.[0-9]+)?\.*$/g, "$1$2")
.split('.');
return (a, b, c, i, r) =>
{
a = prep(a);
b = prep(b);
for (i = 0, r = 0, c = Math.max(a.length, b.length); !r && i++ < c;)
{
r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]);
}
return r;
}
})();
Function returns:
0 if a = b
1 if a > b
-1 if a < b
1.0 = 1.0.0.0.0.0
1.0 < 1.0.1
1.0b1 < 1.0
1.0b = 1.0b
1.1 > 1.0.1b
1.1alpha < 1.1beta
1.1rc1 > 1.1beta
1.1rc1 < 1.1rc2
1.1.0a1 < 1.1a2
1.1.0a10 > 1.1.0a1
1.1.0alpha = 1.1a
1.1.0alpha2 < 1.1b1
1.0001 > 1.00000.1.0.0.0.01
/*use strict*/
const compareVer = ((prep, repl) =>
{
prep = t => ("" + t)
//treat non-numerical characters as lower version
//replacing them with a negative number based on charcode of first character
.replace(/[^0-9\.]+/g, c => "." + (c.replace(/[\W_]+/, "").toLowerCase().charCodeAt(0) - 65536) + ".")
//remove trailing "." and "0" if followed by non-numerical characters (1.0.0b);
.replace(/(?:\.0+)*(\.-[0-9]+)(\.[0-9]+)?\.*$/g, "$1$2")
.split('.');
return (a, b, c, i, r) =>
{
a = prep(a);
b = prep(b);
for (i = 0, r = 0, c = Math.max(a.length, b.length); !r && i++ < c;)
{
r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]);
}
return r;
}
})();
//examples
let list = [
["1.0", "1.0.0.0.0.0"],
["1.0", "1.0.1"],
["1.0b1", "1.0"],
["1.0b", "1.0b"],
["1.1", "1.0.1b"],
["1.1alpha", "1.1beta"],
["1.1rc1", "1.1beta"],
["1.1rc1", "1.1rc2"],
["1.1.0a1", "1.1a2"],
["1.1.0a10", "1.1.0a1"],
["1.1.0alpha", "1.1a"],
["1.1.0alpha2", "1.1b1"],
["1.0001", "1.00000.1.0.0.0.01"]
]
for(let i = 0; i < list.length; i++)
{
console.log( list[i][0] + " " + "<=>"[compareVer(list[i][0], list[i][1]) + 1] + " " + list[i][1] );
}
https://jsfiddle.net/vanowm/p7uvtbor/
Couldn't find a function doing what I wanted here. So I wrote my own. This is my contribution. I hope someone find it useful.
Pros:
Handles version strings of arbitrary length. '1' or '1.1.1.1.1'.
Defaults each value to 0 if not specified. Just because a string is longer doesn't mean it's a bigger version. ('1' should be the same as '1.0' and '1.0.0.0'.)
Compare numbers not strings. ('3'<'21' should be true. Not false.)
Don't waste time on useless compares in the loop. (Comparing for ==)
You can choose your own comparator.
Cons:
It does not handle letters in the version string. (I don't know how that would even work?)
My code, similar to the accepted answer by 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);
}
Examples:
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
2017 answer:
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;
}
Simplest code for modern browsers:
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;
}
The idea here is to compare numbers but in the form of string. to make the comparison work the two strings must be at the same length. so:
"123" > "99" become "123" > "099"
padding the short number "fix" the comparison
Here I padding each part with zeros to lengths of 10. then just use simple string compare for the answer
Example :
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
I faced the similar issue, and I had already created a solution for it. Feel free to give it a try.
It returns 0 for equal, 1 if the version is greater and -1 if it is 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"));
Although this question already has a lot of answers, each one promotes their own backyard-brewn solution, whilst we have a whole ecosystem of (battle-)tested libraries for this.
A quick search on NPM, GitHub, X will give us some lovely libs, and I'd want to run through some:
semver-compare is a great lightweight (~230 bytes) lib that's especially useful if you want to sort by version numbers, as the library's exposed method returns -1, 0 or 1 appropriately.
The core of the library:
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 is rather hefty in size (~4.4 kB gzipped), but allows for some nice unique comparisons like to find the minimum/maximum of a stack of versions or to find out if the provided version is unique or less than anything else in a collection of versions.
compare-versions is another small library (~630 bytes gzipped) and follows the spec nicely, meaning you can compare versions with alpha/beta flags and even wildcards (like for minor/patch versions: 1.0.x or 1.0.*)
The point being: there's not always a need to copy-paste code from Stack Overflow, if you can find decent, (unit-)tested versions via your package manager of choice.
Forgive me if this idea already been visited in a link I have not seen.
I have had some success with conversion of the parts into a weighted sum like so:
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);
Which made comparisons very easy (comparing a double).
Our version fields are never more than 4 digits.
7.10.2.184 -> 7010002184.0
7.11.0.1385 -> 7011001385.0
I hope this helps someone, as the multiple conditionals seem a bit overkill.
We can now use Intl.Collator API now to create numeric comparators. Browser support is pretty decent, but not supported in Node.js at the time of writing.
const semverCompare = new Intl.Collator("en", { numeric: true }).compare;
const versions = ['1.0.1', '1.10.2', '1.1.1', '1.10.1', '1.5.10', '2.10.0', '2.0.1'];
console.log(versions.sort(semverCompare))
const example2 = ["1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"];
console.log(example2.sort(semverCompare))
A dead simple way:
function compareVer(previousVersion, currentVersion) {
try {
const [prevMajor, prevMinor = 0, prevPatch = 0] = previousVersion.split('.').map(Number);
const [curMajor, curMinor = 0, curPatch = 0] = currentVersion.split('.').map(Number);
if (curMajor > prevMajor) {
return 'major update';
}
if (curMajor < prevMajor) {
return 'major downgrade';
}
if (curMinor > prevMinor) {
return 'minor update';
}
if (curMinor < prevMinor) {
return 'minor downgrade';
}
if (curPatch > prevPatch) {
return 'patch update';
}
if (curPatch < prevPatch) {
return 'patch downgrade';
}
return 'same version';
} catch (e) {
return 'invalid format';
}
}
Output:
compareVer("3.1", "3.1.1") // patch update
compareVer("3.1.1", "3.2") // minor update
compareVer("2.1.1", "1.1.1") // major downgrade
compareVer("1.1.1", "1.1.1") // same version
Check the function version_compare() from the php.js project. It's is similar to PHP's version_compare().
You can simply use it like this:
version_compare('2.0', '2.0.0.1', '<');
// returns true
My less verbose answer than most of the answers here
/**
* 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
}
You could use String#localeCompare with options
sensitivity
Which differences in the strings should lead to non-zero result values. Possible values are:
"base": Only strings that differ in base letters compare as unequal. Examples: a ≠ b, a = á, a = A.
"accent": Only strings that differ in base letters or accents and other diacritic marks compare as unequal. Examples: a ≠ b, a ≠ á, a = A.
"case": Only strings that differ in base letters or case compare as unequal. Examples: a ≠ b, a = á, a ≠ A.
"variant": Strings that differ in base letters, accents and other diacritic marks, or case compare as unequal. Other differences may also be taken into consideration. Examples: a ≠ b, a ≠ á, a ≠ A.
The default is "variant" for usage "sort"; it's locale dependent for usage "search".
numeric
Whether numeric collation should be used, such that "1" < "2" < "10". Possible values are true and false; the default is false. This option can be set through an options property or through a Unicode extension key; if both are provided, the options property takes precedence. Implementations are not required to support this property.
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);
The (most of the time) correct JavaScript answer in 2020
Both Nina Scholz in March 2020 and Sid Vishnoi in April 2020 post the modern answer:
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);
localCompare has been around for some time
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator
But what about 1.0a and 1.0.1
localCompare doesn't solve that, still returns 1.0.1 , 1.0a
Michael Deal in his (longish &complex) solution already cracked that in 2013
He converts Numbers to another Base, so they can be sorted better
His answer got me thinking...
666 - Don't think in numbers - 999
Sorting is alphanumeric, based on the ASCII values, so let's (ab)use ASCII as the "base"
My solution is to convert 1.0.2.1 to b.a.c.b to bacb , and then sort
This solves 1.1 vs. 1.0.0.0.1 with: bb vs. baaab
And immediately solves the 1.0a and 1.0.1 sorting problem with notation: baa and bab
Conversion is done with:
const str = s => s.match(/(\d+)|[a-z]/g)
.map(c => c == ~~c ? String.fromCharCode(97 + c) : c);
= Calculate ASCII value for 0...999 Numbers, otherwise concat letter
1.0a >>> [ "1" , "0" , "a" ] >>> [ "b" , "a" , "a" ]
For comparison sake there is no need to concatenate it to one string with .join("")
Oneliner
const sortVersions=(x,v=s=>s.match(/(\d+)|[a-z]/g)
.map(c=>c==~~c?String.fromCharCode(97+c):c))
=>x.sort((a,b)=>v(b)<v(a)?1:-1)
Test snippet:
function log(label,val){
document.body.append(label,String(val).replace(/,/g," - "),document.createElement("BR"));
}
let v = ["1.90.1", "1.9.1", "1.89", "1.090", "1.2", "1.0a", "1.0.1", "1.10", "1.0.0a"];
log('not sorted input :',v);
v.sort((a, b) => a.localeCompare(b,undefined,{numeric:true,sensitivity:'base' }));
log(' locale Compare :', v); // 1.0a AFTER 1.0.1
const str = s => s.match(/(\d+)|[a-z]/g)
.map(c => c == ~~c ? String.fromCharCode(97 + c) : c);
const versionCompare = (a, b) => {
a = str(a);
b = str(b);
return b < a ? 1 : a == b ? 0 : -1;
}
v.sort(versionCompare);
log('versionCompare:', v);
Note how 1.090 is sorted in both results.
My code will not solve the 001.012.001 notation mentioned in one answer, but the localeCompare gets that part of the challenge right.
You could combine the two methods:
sort with .localCompare OR versionCompare when there is a letter involved
Final JavaScript solution
const sortVersions = (
x,
v = s => s.match(/[a-z]|\d+/g).map(c => c==~~c ? String.fromCharCode(97 + c) : c)
) => x.sort((a, b) => (a + b).match(/[a-z]/)
? v(b) < v(a) ? 1 : -1
: a.localeCompare(b, 0, {numeric: true}))
let v=["1.90.1","1.090","1.0a","1.0.1","1.0.0a","1.0.0b","1.0.0.1"];
console.log(sortVersions(v));
Few lines of code and good if you don't want to allow letters or symbols. This works if you control the versioning scheme and it's not something a 3rd party provides.
// we presume all versions are of this format "1.4" or "1.10.2.3", without letters
// returns: 1 (bigger), 0 (same), -1 (smaller)
function versionCompare (v1, v2) {
const v1Parts = v1.split('.')
const v2Parts = v2.split('.')
const length = Math.max(v1Parts.length, v2Parts.length)
for (let i = 0; i < length; i++) {
const value = (parseInt(v1Parts[i]) || 0) - (parseInt(v2Parts[i]) || 0)
if (value < 0) return -1
if (value > 0) return 1
}
return 0
}
console.log(versionCompare('1.2.0', '1.2.4') === -1)
console.log(versionCompare('1.2', '1.2.0') === 0)
console.log(versionCompare('1.2', '1') === 1)
console.log(versionCompare('1.2.10', '1.2.1') === 1)
console.log(versionCompare('1.2.134230', '1.2.2') === 1)
console.log(versionCompare('1.2.134230', '1.3.0.1.2.3.1') === -1)
You can use a JavaScript localeCompare method:
a.localeCompare(b, undefined, { numeric: true })
Here is an example:
"1.1".localeCompare("2.1.1", undefined, { numeric: true }) => -1
"1.0.0".localeCompare("1.0", undefined, { numeric: true }) => 1
"1.0.0".localeCompare("1.0.0", undefined, { numeric: true }) => 0
// 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
}
The idea is to compare two versions and know which is the biggest. We delete "." and we compare each position of the vector with the other.
// 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;
}
}
}
The replace() function only replaces the first occurence in the string. So, lets replace the . with ,. Afterwards delete all . and make the , to . again and parse it to float.
for(i=0; i<versions.length; i++) {
v = versions[i].replace('.', ',');
v = v.replace(/\./g, '');
versions[i] = parseFloat(v.replace(',', '.'));
}
finally, sort it:
versions.sort();
Check out this blog post. This function works for numeric version numbers.
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
If, for example, we want to check if the current jQuery version is less than 1.8, parseFloat($.ui.version) < 1.8 ) would give a wrong result if version is "1.10.1", since parseFloat("1.10.1") returns 1.1.
A string compare would also go wrong, since "1.8" < "1.10" evaluates to false.
So we need a test like this
if(versionCompare($.ui.version, "1.8") < 0){
alert("please update jQuery");
}
The following function handles this correctly:
/** 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;
}
Here are some examples:
// 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);
See here for a live sample and test suite:
http://jsfiddle.net/mar10/8KjvP/
This is a neat trick. If you are dealing with numeric values, between a specific range of values, you can assign a value to each level of the version object. For instance "largestValue" is set to 0xFF here, which creates a very "IP" sort of look to your versioning.
This also handles alpha-numeric versioning (i.e. 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);
I made this based on Kons idea, and optimized it for Java version "1.7.0_45". It's just a function meant to convert a version string to a float. This is the function:
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;
}
String "1.7.0_45" is converted to 1.0070000450000001 and this is good enough for a normal comparison. Error explained here: How to deal with floating point number precision in JavaScript?. If need more then 3 digits on any part you can change the divider Math.pow(10, i * 3);.
Output will look like this:
1.7.0_45 > 1.007000045
ver 1.7.build_45 > 1.007000045
1.234.567.890 > 1.23456789
Here's a coffeescript implementation suitable for use with Array.sort inspired by other answers here:
# 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
I wrote a node module for sorting versions, you can find it here: version-sort
Features:
no limit of sequences '1.0.1.5.53.54654.114.1.154.45' works
no limit of sequence length: '1.1546515465451654654654654138754431574364321353734' works
can sort objects by version (see README)
stages (like alpha, beta, rc1, rc2)
Do not hesitate to open an issue if you need an other feature.

Inserting into a number string

Have the function DashInsert(num) insert dashes ('-') between each two odd numbers in num. For example: if num is 454793 the output should be 4547-9-3. Don't count zero as an odd number.
Here is my code (not working). When I run it, I get the same response as an infinite loop where I have to kill the page but I can't see why. I know there are ways to do this by keeping it as a string but now I'm wondering why my way isn't working. Thanks...
function DashInsert(num) {
num = num.split("");
for (i = 1; i < num.length; i++) {
if (num[i - 1] % 2 != 0 && num[i] % 2 != 0) {
num.splice(i, 0, "-");
}
}
num = num.join("");
return num;
}
Using num.splice you are inserting new entries into the array, therefor increasing its length – and that makes the value of i “running behind” the increasing length of the array, so the break condition is never met.
And apart from that, on the next iteration after inserting a -, num[i-1] will be that - character, and therefor you are practically trying to check if '-' % 2 != 0 … that makes little sense as well.
So, when you insert a - into the array, you have to increase i by one as well – that will a) account for the length of the array having increased by one, and also it will check the next digit after the - on the next iteration:
function DashInsert(num) {
num = num.split("");
for (i = 1; i < num.length; i++) {
if (num[i - 1] % 2 != 0 && num[i] % 2 != 0) {
num.splice(i, 0, "-");
i++; // <- this is the IMPORTANT part!
}
}
num = num.join("");
return num;
}
alert(DashInsert("454793"));
http://jsfiddle.net/37wA9/
Once you insert a dash -, the if statement is checking this '-'%2 != 0 which is always true and thus inserts another dash, ad infinitum.
Here's one way to do it with replace using a regex and function:
function DashInsert(n) {
var f = function(m,i,s) { return m&s[i+1]&1 ? m+'-' : m; };
return String(n).replace(/\d/g,f);
}
DashInsert(454793) // "4547-9-3"
When you are adding a dash, this dash will be processed as a number on the next iteration. You need to forward one step.
function DashInsert(num) {
var num = num.split("");
for (var i = 1; i < num.length; i++) {
if ((num[i - 1] % 2 != 0) && (num[i] % 2 != 0)) {
num.splice(i, 0, "-");
i++; // This is the only thing that needs changing
}
}
num = num.join("");
return num;
}
It's because there are cases when you use the % operator on dash '-' itself, e.g. right after you splice a dash into the array.
You can correct this behavior by using a clone array.
function DashInsert(num) {
num = num.split("");
var clone = num.slice(0);
var offset = 0;
for (i = 1; i < num.length; i++) {
if (num[i - 1] % 2 != 0 && num[i] % 2 != 0) {
clone.splice(i + offset, 0, "-");
offset++;
}
}
return clone.join("");
}
alert(DashInsert("45739"));
Output: 45-7-3-9
Demo: http://jsfiddle.net/262Bf/
To complement the great answers already given, I would like to share an alternative implementation, that doesn't modify arrays in-place:
function DashInsert(num) {
var characters = num.split("");
var numbers = characters.map(function(chr) {
return parseInt(chr, 10);
});
var withDashes = numbers.reduce(function(result, current) {
var lastNumber = result[result.length - 1];
if(lastNumber == null || current % 2 === 0 || lastNumber % 2 === 0) {
return result.concat(current);
} else {
return result.concat("-", current);
}
}, []);
return withDashes.join("");
}
It's longer, but IMHO reveals the intention better, and avoids the original issue.

How to sort strings in JavaScript numerically

I would like to sort an array of strings (in JavaScript) such that groups of digits within the strings are compared as integers not strings. I am not worried about signed or floating point numbers.
For example, the result should be ["a1b3","a9b2","a10b2","a10b11"] not ["a1b3","a10b11","a10b2","a9b2"]
The easiest way to do this seems to be splitting each string on boundaries around groups of digits. Is there a pattern I can pass to String.split to split on character boundaries without removing any characters?
"abc11def22ghi".split(/?/) = ["abc","11","def","22","ghi"];
Or is there another way to compare strings that does not involve splitting them up, perhaps by padding all groups of digits with leading zeros so they are the same length?
"aa1bb" => "aa00000001bb", "aa10bb" => "aa00000010bb"
I am working with arbitrary strings, not strings that have a specific arrangement of digit groups.
I like the /(\d+)/ one liner from Gaby to split the array. How backwards compatible is that?
The solutions that parse the strings once in a way that can be used to rebuild the originals are much more efficient that this compare function. None of the answers handle some strings starting with digits and others not, but that would be easy enough to remedy and was not explicit in the original question.
["a100", "a20", "a3", "a3b", "a3b100", "a3b20", "a3b3", "!!", "~~", "9", "10", "9.5"].sort(function (inA, inB) {
var result = 0;
var a, b, pattern = /(\d+)/;
var as = inA.split(pattern);
var bs = inB.split(pattern);
var index, count = as.length;
if (('' === as[0]) === ('' === bs[0])) {
if (count > bs.length)
count = bs.length;
for (index = 0; index < count && 0 === result; ++index) {
a = as[index]; b = bs[index];
if (index & 1) {
result = a - b;
} else {
result = !(a < b) ? (a > b) ? 1 : 0 : -1;
}
}
if (0 === result)
result = as.length - bs.length;
} else {
result = !(inA < inB) ? (inA > inB) ? 1 : 0 : -1;
}
return result;
}).toString();
Result: "!!,9,9.5,10,a3,a3b,a3b3,a3b20,a3b100,a20,a100,~~"
Another variant is to use an instance of Intl.Collator with the numeric option:
var array = ["a100", "a20", "a3", "a3b", "a3b100", "a3b20", "a3b3", "!!", "~~", "9", "10", "9.5"];
var collator = new Intl.Collator([], {numeric: true});
array.sort((a, b) => collator.compare(a, b));
console.log(array);
I think this does what you want
function sortArray(arr) {
var tempArr = [], n;
for (var i in arr) {
tempArr[i] = arr[i].match(/([^0-9]+)|([0-9]+)/g);
for (var j in tempArr[i]) {
if( ! isNaN(n = parseInt(tempArr[i][j])) ){
tempArr[i][j] = n;
}
}
}
tempArr.sort(function (x, y) {
for (var i in x) {
if (y.length < i || x[i] < y[i]) {
return -1; // x is longer
}
if (x[i] > y[i]) {
return 1;
}
}
return 0;
});
for (var i in tempArr) {
arr[i] = tempArr[i].join('');
}
return arr;
}
alert(
sortArray(["a1b3", "a10b11", "a10b2", "a9b2"]).join(",")
);
Assuming you want to just do a numeric sort by the digits in each array entry (ignoring the non-digits), you can use this:
function sortByDigits(array) {
var re = /\D/g;
array.sort(function(a, b) {
return(parseInt(a.replace(re, ""), 10) - parseInt(b.replace(re, ""), 10));
});
return(array);
}
It uses a custom sort function that removes the digits and converts to a number each time it's asked to do a comparison. You can see it work here: http://jsfiddle.net/jfriend00/t87m2/.
Use this compare function for sorting...
function compareLists(a, b) {
var alist = a.split(/(\d+)/), // Split text on change from anything
// to digit and digit to anything
blist = b.split(/(\d+)/); // Split text on change from anything
// to digit and digit to anything
alist.slice(-1) == '' ? alist.pop() : null; // Remove the last element if empty
blist.slice(-1) == '' ? blist.pop() : null; // Remove the last element if empty
for (var i = 0, len = alist.length; i < len; i++) {
if (alist[i] != blist[i]){ // Find the first non-equal part
if (alist[i].match(/\d/)) // If numeric
{
return +alist[i] - +blist[i]; // Compare as number
} else {
return alist[i].localeCompare(blist[i]); // Compare as string
}
}
}
return true;
}
Syntax
var data = ["a1b3", "a10b11", "b10b2", "a9b2", "a1b20", "a1c4"];
data.sort(compareLists);
alert(data);
There is a demo at http://jsfiddle.net/h9Rqr/7/.
Here's a more complete solution that sorts according to both letters and numbers in the strings
function sort(list) {
var i, l, mi, ml, x;
// copy the original array
list = list.slice(0);
// split the strings, converting numeric (integer) parts to integers
// and leaving letters as strings
for( i = 0, l = list.length; i < l; i++ ) {
list[i] = list[i].match(/(\d+|[a-z]+)/g);
for( mi = 0, ml = list[i].length; mi < ml ; mi++ ) {
x = parseInt(list[i][mi], 10);
list[i][mi] = !!x || x === 0 ? x : list[i][mi];
}
}
// sort deeply, without comparing integers as strings
list = list.sort(function(a, b) {
var i = 0, l = a.length, res = 0;
while( res === 0 && i < l) {
if( a[i] !== b[i] ) {
res = a[i] < b[i] ? -1 : 1;
break;
}
// If you want to ignore the letters, and only sort by numbers
// use this instead:
//
// if( typeof a[i] === "number" && a[i] !== b[i] ) {
// res = a[i] < b[i] ? -1 : 1;
// break;
// }
i++;
}
return res;
});
// glue it together again
for( i = 0, l = list.length; i < l; i++ ) {
list[i] = list[i].join("");
}
return list;
}
I needed a way to take a mixed string and create a string that could be sorted elsewhere, so that numbers sorted numerically and letters alphabetically. Based on answers above I created the following, which pads out all numbers in a way I can understand, wherever they appear in the string.
function padAllNumbers(strIn) {
// Used to create mixed strings that sort numerically as well as non-numerically
var patternDigits = /(\d+)/g; // This recognises digit/non-digit boundaries
var astrIn = strIn.split( patternDigits ); // we create an array of alternating digit/non-digit groups
var result = "";
for (var i=0;i<astrIn.length; i++) {
if (astrIn[i] != "") { // first and last elements can be "" and we don't want these padded out
if (isNaN(astrIn[i])) {
result += astrIn[i];
} else {
result += padOneNumberString("000000000",astrIn[i]);
}
}
}
return result;
}
function padOneNumberString(pad,strNum,left) {
// Pad out a string at left (or right)
if (typeof strNum === "undefined") return pad;
if (typeof left === "undefined") left = true;
var padLen = pad.length - (""+ strNum).length;
var padding = pad.substr(0,padLen);
return left? padding + strNum : strNum + padding;
}
Sorting occurs from left to right unless you create a custom algorithm. Letters or digits are compared digits first and then letters.
However, what you want to accomplish as per your own example (a1, a9, a10) won’t ever happen. That would require you knowing the data beforehand and splitting the string in every possible way before applying the sorting.
One final alternative would be:
a) break each and every string from left to right whenever there is a change from letter to digit and vice versa; &
b) then start the sorting on those groups from right-to-left. That will be a very demanding algorithm. Can be done!
Finally, if you are the generator of the original "text", you should consider NORMALIZING the output where a1 a9 a10 could be outputted as a01 a09 a10. This way you could have full control of the final version of the algorithm.

How can I compare software version number using JavaScript? (only numbers)

Here is the software version number:
"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"
How can I compare this?
Assume the correct order is:
"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"
The idea is simple...:
Read the first digit, than, the second, after that the third...
But I can't convert the version number to float number...
You also can see the version number like this:
"1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1.0"
And this is clearer to see what is the idea behind...
But, how can I convert it into a computer program?
semver
The semantic version parser used by 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, '*')
Semantic Versioning Link : https://www.npmjs.com/package/semver#prerelease-identifiers
The basic idea to make this comparison would be to use Array.split to get arrays of parts from the input strings and then compare pairs of parts from the two arrays; if the parts are not equal we know which version is smaller.
There are a few of important details to keep in mind:
How should the parts in each pair be compared? The question wants to compare numerically, but what if we have version strings that are not made up of just digits (e.g. "1.0a")?
What should happen if one version string has more parts than the other? Most likely "1.0" should be considered less than "1.0.1", but what about "1.0.0"?
Here's the code for an implementation that you can use directly (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;
}
This version compares parts naturally, does not accept character suffixes and considers "1.7" to be smaller than "1.7.0". The comparison mode can be changed to lexicographical and shorter version strings can be automatically zero-padded using the optional third argument.
There is a JSFiddle that runs "unit tests" here; it is a slightly expanded version of ripper234's work (thank you).
Important note: This code uses Array.map and Array.every, which means that it will not run in IE versions earlier than 9. If you need to support those you will have to provide polyfills for the missing methods.
The simplest is to use localeCompare :
a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })
This will return:
0: version strings are equal
1: version a is greater than b
-1: version b is greater than a
// 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"));
This very small, yet very fast compare function takes version numbers of any length and any number size per segment.
Return values:
- a number < 0 if a < b
- a number > 0 if a > b
- 0 if a = b
So you can use it as compare function for Array.sort();
EDIT: Bugfixed Version stripping trailing zeros to recognize "1" and "1.0.0" as equal
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"]
Simple and short function:
function isNewerVersion (oldVer, newVer) {
const oldParts = oldVer.split('.')
const newParts = newVer.split('.')
for (var i = 0; i < newParts.length; i++) {
const a = ~~newParts[i] // parse int
const b = ~~oldParts[i] // parse int
if (a > b) return true
if (a < b) return false
}
return false
}
Tests:
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
Taken from 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;
}
Here is another short version that works with any number of sub versions, padded zeros and even numbers with letters (1.0.0b3)
const compareVer = ((prep, repl) =>
{
prep = t => ("" + t)
//treat non-numerical characters as lower version
//replacing them with a negative number based on charcode of first character
.replace(/[^0-9\.]+/g, c => "." + (c.replace(/[\W_]+/, "").toLowerCase().charCodeAt(0) - 65536) + ".")
//remove trailing "." and "0" if followed by non-numerical characters (1.0.0b);
.replace(/(?:\.0+)*(\.-[0-9]+)(\.[0-9]+)?\.*$/g, "$1$2")
.split('.');
return (a, b, c, i, r) =>
{
a = prep(a);
b = prep(b);
for (i = 0, r = 0, c = Math.max(a.length, b.length); !r && i++ < c;)
{
r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]);
}
return r;
}
})();
Function returns:
0 if a = b
1 if a > b
-1 if a < b
1.0 = 1.0.0.0.0.0
1.0 < 1.0.1
1.0b1 < 1.0
1.0b = 1.0b
1.1 > 1.0.1b
1.1alpha < 1.1beta
1.1rc1 > 1.1beta
1.1rc1 < 1.1rc2
1.1.0a1 < 1.1a2
1.1.0a10 > 1.1.0a1
1.1.0alpha = 1.1a
1.1.0alpha2 < 1.1b1
1.0001 > 1.00000.1.0.0.0.01
/*use strict*/
const compareVer = ((prep, repl) =>
{
prep = t => ("" + t)
//treat non-numerical characters as lower version
//replacing them with a negative number based on charcode of first character
.replace(/[^0-9\.]+/g, c => "." + (c.replace(/[\W_]+/, "").toLowerCase().charCodeAt(0) - 65536) + ".")
//remove trailing "." and "0" if followed by non-numerical characters (1.0.0b);
.replace(/(?:\.0+)*(\.-[0-9]+)(\.[0-9]+)?\.*$/g, "$1$2")
.split('.');
return (a, b, c, i, r) =>
{
a = prep(a);
b = prep(b);
for (i = 0, r = 0, c = Math.max(a.length, b.length); !r && i++ < c;)
{
r = -1 * ((a[i] = ~~a[i]) < (b[i] = ~~b[i])) + (a[i] > b[i]);
}
return r;
}
})();
//examples
let list = [
["1.0", "1.0.0.0.0.0"],
["1.0", "1.0.1"],
["1.0b1", "1.0"],
["1.0b", "1.0b"],
["1.1", "1.0.1b"],
["1.1alpha", "1.1beta"],
["1.1rc1", "1.1beta"],
["1.1rc1", "1.1rc2"],
["1.1.0a1", "1.1a2"],
["1.1.0a10", "1.1.0a1"],
["1.1.0alpha", "1.1a"],
["1.1.0alpha2", "1.1b1"],
["1.0001", "1.00000.1.0.0.0.01"]
]
for(let i = 0; i < list.length; i++)
{
console.log( list[i][0] + " " + "<=>"[compareVer(list[i][0], list[i][1]) + 1] + " " + list[i][1] );
}
https://jsfiddle.net/vanowm/p7uvtbor/
Couldn't find a function doing what I wanted here. So I wrote my own. This is my contribution. I hope someone find it useful.
Pros:
Handles version strings of arbitrary length. '1' or '1.1.1.1.1'.
Defaults each value to 0 if not specified. Just because a string is longer doesn't mean it's a bigger version. ('1' should be the same as '1.0' and '1.0.0.0'.)
Compare numbers not strings. ('3'<'21' should be true. Not false.)
Don't waste time on useless compares in the loop. (Comparing for ==)
You can choose your own comparator.
Cons:
It does not handle letters in the version string. (I don't know how that would even work?)
My code, similar to the accepted answer by 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);
}
Examples:
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
2017 answer:
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;
}
Simplest code for modern browsers:
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;
}
The idea here is to compare numbers but in the form of string. to make the comparison work the two strings must be at the same length. so:
"123" > "99" become "123" > "099"
padding the short number "fix" the comparison
Here I padding each part with zeros to lengths of 10. then just use simple string compare for the answer
Example :
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
I faced the similar issue, and I had already created a solution for it. Feel free to give it a try.
It returns 0 for equal, 1 if the version is greater and -1 if it is 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"));
Although this question already has a lot of answers, each one promotes their own backyard-brewn solution, whilst we have a whole ecosystem of (battle-)tested libraries for this.
A quick search on NPM, GitHub, X will give us some lovely libs, and I'd want to run through some:
semver-compare is a great lightweight (~230 bytes) lib that's especially useful if you want to sort by version numbers, as the library's exposed method returns -1, 0 or 1 appropriately.
The core of the library:
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 is rather hefty in size (~4.4 kB gzipped), but allows for some nice unique comparisons like to find the minimum/maximum of a stack of versions or to find out if the provided version is unique or less than anything else in a collection of versions.
compare-versions is another small library (~630 bytes gzipped) and follows the spec nicely, meaning you can compare versions with alpha/beta flags and even wildcards (like for minor/patch versions: 1.0.x or 1.0.*)
The point being: there's not always a need to copy-paste code from Stack Overflow, if you can find decent, (unit-)tested versions via your package manager of choice.
Forgive me if this idea already been visited in a link I have not seen.
I have had some success with conversion of the parts into a weighted sum like so:
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);
Which made comparisons very easy (comparing a double).
Our version fields are never more than 4 digits.
7.10.2.184 -> 7010002184.0
7.11.0.1385 -> 7011001385.0
I hope this helps someone, as the multiple conditionals seem a bit overkill.
We can now use Intl.Collator API now to create numeric comparators. Browser support is pretty decent, but not supported in Node.js at the time of writing.
const semverCompare = new Intl.Collator("en", { numeric: true }).compare;
const versions = ['1.0.1', '1.10.2', '1.1.1', '1.10.1', '1.5.10', '2.10.0', '2.0.1'];
console.log(versions.sort(semverCompare))
const example2 = ["1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"];
console.log(example2.sort(semverCompare))
A dead simple way:
function compareVer(previousVersion, currentVersion) {
try {
const [prevMajor, prevMinor = 0, prevPatch = 0] = previousVersion.split('.').map(Number);
const [curMajor, curMinor = 0, curPatch = 0] = currentVersion.split('.').map(Number);
if (curMajor > prevMajor) {
return 'major update';
}
if (curMajor < prevMajor) {
return 'major downgrade';
}
if (curMinor > prevMinor) {
return 'minor update';
}
if (curMinor < prevMinor) {
return 'minor downgrade';
}
if (curPatch > prevPatch) {
return 'patch update';
}
if (curPatch < prevPatch) {
return 'patch downgrade';
}
return 'same version';
} catch (e) {
return 'invalid format';
}
}
Output:
compareVer("3.1", "3.1.1") // patch update
compareVer("3.1.1", "3.2") // minor update
compareVer("2.1.1", "1.1.1") // major downgrade
compareVer("1.1.1", "1.1.1") // same version
Check the function version_compare() from the php.js project. It's is similar to PHP's version_compare().
You can simply use it like this:
version_compare('2.0', '2.0.0.1', '<');
// returns true
My less verbose answer than most of the answers here
/**
* 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
}
You could use String#localeCompare with options
sensitivity
Which differences in the strings should lead to non-zero result values. Possible values are:
"base": Only strings that differ in base letters compare as unequal. Examples: a ≠ b, a = á, a = A.
"accent": Only strings that differ in base letters or accents and other diacritic marks compare as unequal. Examples: a ≠ b, a ≠ á, a = A.
"case": Only strings that differ in base letters or case compare as unequal. Examples: a ≠ b, a = á, a ≠ A.
"variant": Strings that differ in base letters, accents and other diacritic marks, or case compare as unequal. Other differences may also be taken into consideration. Examples: a ≠ b, a ≠ á, a ≠ A.
The default is "variant" for usage "sort"; it's locale dependent for usage "search".
numeric
Whether numeric collation should be used, such that "1" < "2" < "10". Possible values are true and false; the default is false. This option can be set through an options property or through a Unicode extension key; if both are provided, the options property takes precedence. Implementations are not required to support this property.
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);
The (most of the time) correct JavaScript answer in 2020
Both Nina Scholz in March 2020 and Sid Vishnoi in April 2020 post the modern answer:
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);
localCompare has been around for some time
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator
But what about 1.0a and 1.0.1
localCompare doesn't solve that, still returns 1.0.1 , 1.0a
Michael Deal in his (longish &complex) solution already cracked that in 2013
He converts Numbers to another Base, so they can be sorted better
His answer got me thinking...
666 - Don't think in numbers - 999
Sorting is alphanumeric, based on the ASCII values, so let's (ab)use ASCII as the "base"
My solution is to convert 1.0.2.1 to b.a.c.b to bacb , and then sort
This solves 1.1 vs. 1.0.0.0.1 with: bb vs. baaab
And immediately solves the 1.0a and 1.0.1 sorting problem with notation: baa and bab
Conversion is done with:
const str = s => s.match(/(\d+)|[a-z]/g)
.map(c => c == ~~c ? String.fromCharCode(97 + c) : c);
= Calculate ASCII value for 0...999 Numbers, otherwise concat letter
1.0a >>> [ "1" , "0" , "a" ] >>> [ "b" , "a" , "a" ]
For comparison sake there is no need to concatenate it to one string with .join("")
Oneliner
const sortVersions=(x,v=s=>s.match(/(\d+)|[a-z]/g)
.map(c=>c==~~c?String.fromCharCode(97+c):c))
=>x.sort((a,b)=>v(b)<v(a)?1:-1)
Test snippet:
function log(label,val){
document.body.append(label,String(val).replace(/,/g," - "),document.createElement("BR"));
}
let v = ["1.90.1", "1.9.1", "1.89", "1.090", "1.2", "1.0a", "1.0.1", "1.10", "1.0.0a"];
log('not sorted input :',v);
v.sort((a, b) => a.localeCompare(b,undefined,{numeric:true,sensitivity:'base' }));
log(' locale Compare :', v); // 1.0a AFTER 1.0.1
const str = s => s.match(/(\d+)|[a-z]/g)
.map(c => c == ~~c ? String.fromCharCode(97 + c) : c);
const versionCompare = (a, b) => {
a = str(a);
b = str(b);
return b < a ? 1 : a == b ? 0 : -1;
}
v.sort(versionCompare);
log('versionCompare:', v);
Note how 1.090 is sorted in both results.
My code will not solve the 001.012.001 notation mentioned in one answer, but the localeCompare gets that part of the challenge right.
You could combine the two methods:
sort with .localCompare OR versionCompare when there is a letter involved
Final JavaScript solution
const sortVersions = (
x,
v = s => s.match(/[a-z]|\d+/g).map(c => c==~~c ? String.fromCharCode(97 + c) : c)
) => x.sort((a, b) => (a + b).match(/[a-z]/)
? v(b) < v(a) ? 1 : -1
: a.localeCompare(b, 0, {numeric: true}))
let v=["1.90.1","1.090","1.0a","1.0.1","1.0.0a","1.0.0b","1.0.0.1"];
console.log(sortVersions(v));
Few lines of code and good if you don't want to allow letters or symbols. This works if you control the versioning scheme and it's not something a 3rd party provides.
// we presume all versions are of this format "1.4" or "1.10.2.3", without letters
// returns: 1 (bigger), 0 (same), -1 (smaller)
function versionCompare (v1, v2) {
const v1Parts = v1.split('.')
const v2Parts = v2.split('.')
const length = Math.max(v1Parts.length, v2Parts.length)
for (let i = 0; i < length; i++) {
const value = (parseInt(v1Parts[i]) || 0) - (parseInt(v2Parts[i]) || 0)
if (value < 0) return -1
if (value > 0) return 1
}
return 0
}
console.log(versionCompare('1.2.0', '1.2.4') === -1)
console.log(versionCompare('1.2', '1.2.0') === 0)
console.log(versionCompare('1.2', '1') === 1)
console.log(versionCompare('1.2.10', '1.2.1') === 1)
console.log(versionCompare('1.2.134230', '1.2.2') === 1)
console.log(versionCompare('1.2.134230', '1.3.0.1.2.3.1') === -1)
You can use a JavaScript localeCompare method:
a.localeCompare(b, undefined, { numeric: true })
Here is an example:
"1.1".localeCompare("2.1.1", undefined, { numeric: true }) => -1
"1.0.0".localeCompare("1.0", undefined, { numeric: true }) => 1
"1.0.0".localeCompare("1.0.0", undefined, { numeric: true }) => 0
// 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
}
The idea is to compare two versions and know which is the biggest. We delete "." and we compare each position of the vector with the other.
// 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;
}
}
}
The replace() function only replaces the first occurence in the string. So, lets replace the . with ,. Afterwards delete all . and make the , to . again and parse it to float.
for(i=0; i<versions.length; i++) {
v = versions[i].replace('.', ',');
v = v.replace(/\./g, '');
versions[i] = parseFloat(v.replace(',', '.'));
}
finally, sort it:
versions.sort();
Check out this blog post. This function works for numeric version numbers.
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
If, for example, we want to check if the current jQuery version is less than 1.8, parseFloat($.ui.version) < 1.8 ) would give a wrong result if version is "1.10.1", since parseFloat("1.10.1") returns 1.1.
A string compare would also go wrong, since "1.8" < "1.10" evaluates to false.
So we need a test like this
if(versionCompare($.ui.version, "1.8") < 0){
alert("please update jQuery");
}
The following function handles this correctly:
/** 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;
}
Here are some examples:
// 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);
See here for a live sample and test suite:
http://jsfiddle.net/mar10/8KjvP/
This is a neat trick. If you are dealing with numeric values, between a specific range of values, you can assign a value to each level of the version object. For instance "largestValue" is set to 0xFF here, which creates a very "IP" sort of look to your versioning.
This also handles alpha-numeric versioning (i.e. 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);
I made this based on Kons idea, and optimized it for Java version "1.7.0_45". It's just a function meant to convert a version string to a float. This is the function:
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;
}
String "1.7.0_45" is converted to 1.0070000450000001 and this is good enough for a normal comparison. Error explained here: How to deal with floating point number precision in JavaScript?. If need more then 3 digits on any part you can change the divider Math.pow(10, i * 3);.
Output will look like this:
1.7.0_45 > 1.007000045
ver 1.7.build_45 > 1.007000045
1.234.567.890 > 1.23456789
Here's a coffeescript implementation suitable for use with Array.sort inspired by other answers here:
# 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
I wrote a node module for sorting versions, you can find it here: version-sort
Features:
no limit of sequences '1.0.1.5.53.54654.114.1.154.45' works
no limit of sequence length: '1.1546515465451654654654654138754431574364321353734' works
can sort objects by version (see README)
stages (like alpha, beta, rc1, rc2)
Do not hesitate to open an issue if you need an other feature.

Get version number from String in Javascript?

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?
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)
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;
}
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.
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
function VersionValue(var str)
{
var tmp = str.split('.');
return (tmp[0] * 100) + (tmp[1] * 10) + tmp[2];
}
if (VersionValue(version) > VersionValue(otherVersion))...
for example
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
function isCorrectVersion(used,required){
var used = parseFloat("0."+used.replace(/\./gi,""));
var required = parseFloat("0."+required.replace(/\./gi,""));
return (used < required) ? false : true;
}
I use this to compare jQuery functions and it seems to work fine, also comparing for example 1.4 with 1.4.1 or 1.4.1 with 1.4.11.
I couldnt find an answer that returns 1, 0 or -1 and takes care of both trailing .0 and two digit partials, so here goes. This should support all cases where all the partials are numbers (see the tests at the bottom).
/*
* Returns 1 if v1 is newer, -1 if v2 is newer and 0 if they are equal.
* .0s at the end of the version will be ignored.
*
* If a version evaluates to false it will be treated as 0.
*
* Examples:
* compareVersions ("2.0", "2") outputs 0,
* compareVersions ("2.0.1", "2") outputs 1,
* compareVersions ("0.2", "0.12.1") outputs -1,
*
*/
function compareVersions (version1, version2) {
var version1 = version1 ? version1.split('.') : ['0'],
version2 = version2 ? version2.split('.') : ['0'],
longest = Math.max(version1.length, version2.length);
for (var i = 0; i < longest; i++) {
/*
* Convert to ints so that we can compare two digit parts
* properly. (Otherwise would "2" be greater than "12").
*
* This returns NaN if the value is undefined, so we check for
* NaN later.
*/
var v1Part = parseInt(version1[i]),
v2Part = parseInt(version2[i]);
if (v1Part != v2Part) {
// version2 is longer
if (isNaN(v1Part)) {
/*
* Go through the rest of the parts of version 2. If it is only zeros,
* consider the versions equal, otherwise consider version 2 as newer.
*/
for (var j = i; j < longest; j++) {
if (parseInt(version2[j]) != 0) return -1;
}
// version1 is longer
} else if (isNaN(v2Part)) {
for (var j = i; j < longest; j++) {
if (parseInt(version1[j]) != 0) return 1;
}
// versions are equally long
} else {
return (v1Part > v2Part) ? 1 : -1;
}
return 0;
}
}
return 0;
}
console.log(compareVersions("1", "1") === 0);
console.log(compareVersions("1.1", "1") === 1);
console.log(compareVersions("1.1.1", "1") === 1);
console.log(compareVersions("1", "1.1.1") === -1);
console.log(compareVersions("0.3", "0.3.0.0.1") === -1);
console.log(compareVersions("0.3", "0.3.0") === 0);
console.log(compareVersions("0.3.0.0.1", "0.3") === 1);
console.log(compareVersions("0.3.0", "0.3") === 0);
console.log(compareVersions("0.12", "0.2") === 1);
console.log(compareVersions("0.2", "0.12") === -1);
console.log(compareVersions("0.12.0", "0.2") === 1);
console.log(compareVersions("0.02.0", "0.2") === 0);
console.log(compareVersions("0.01.0", "0.2") === -1);
With one of the comparison operators.
"1.2.3" > "1.2.4" //false
"1.2.3" < "1.2.4" //true
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').
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.

Categories