How to get the maximum value of strings like "1.2.5.4" and "1.2.5.3"?

I have an array with strings consisting of numbers in dotted notation as such:

var arr = ['1.2.5.4', '1.2.5.3'];

I would like to find a string that indicates the highest value.

I've already tried

Math.max.apply(null, arr)

Since there are several decimal places, this simply will not work.

+3
source share
4 answers

This seems like a simpler solution:

function latest (versions) {
  versions.sort (function (a, b) {
    a = a.split ('.');
    b = b.split ('.');   
    for (var i = 0; i < a.length && i < b.length && a[i] === b[i]; i++);
    return ((i === a.length) || (+a[i] < +b[i])) ? 1 : -1;
  });
  return versions[0]; 
}

[
  latest (['1.2.5.4', '1.3.5.3', '1.2.3.4.5', '1.24.2.1', '1.2.52']),
  latest (['1.2.5.4', '1.3.5.3', '1.2.3.4.5', '1.2.52']),
  latest (['1.2.5.4', '1.2.3.4.5', '1.2.52']),
  latest (['1.2.5.4', '1.2.3.4.5'])
]

/* Displays on JS console

    ["1.24.2.1", "1.3.5.3", "1.2.52", "1.2.5.4"]
*/

Thank you for voting, in the review I noted a bug that I fixed in the code above.

+1
source

To do this, you need to create your own sort routine, as they should be treated as version numbers. You want to make comparisons on the left and on the left, comparing each number bounded by periods.

, , , :

function assert(x) {
    if (!x) {
        alert("Assert failed");
        debugger;
    }
}

function isPositiveInteger(x) {
    // https://stackoverflow.com/a/1019526/11236
    return /^\d+$/.test(x);
}

/**
 * Compare two software version numbers (e.g. 1.7.1)
 * Returns:
 *
 *  0 if they're identical
 *  negative if v1 < v2
 *  positive if v1 > v2
 *  Nan if they in the wrong format
 *
 *  E.g.:
 *
 *  assert(version_number_compare("1.7.1", "1.6.10") > 0);
 *  assert(version_number_compare("1.7.1", "1.7.10") < 0);
 *
 *  "Unit tests": http://jsfiddle.net/ripper234/Xv9WL/28/
 *
 *  Taken from https://stackoverflow.com/a/6832721/11236
 */
function compareVersionNumbers(v1, v2){
    var v1parts = v1.split('.');
    var v2parts = v2.split('.');

    // First, validate both numbers are true version numbers
    function validateParts(parts) {
        for (var i = 0; i < parts.length; ++i) {
            if (!isPositiveInteger(parts[i])) {
                return false;
            }
        }
        return true;
    }
    if (!validateParts(v1parts) || !validateParts(v2parts)) {
        return NaN;
    }

    for (var i = 0; i < v1parts.length; ++i) {
        if (v2parts.length === i) {
            return 1;
        }

        if (v1parts[i] === v2parts[i]) {
            continue;
        }
        if (v1parts[i] > v2parts[i]) {
            return 1;
        }
        return -1;
    }

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

    return 0;
}


assert(compareVersionNumbers("1.7.1", "1.7.10") < 0);
assert(compareVersionNumbers("1.6.1", "1.7.10") < 0);
assert(compareVersionNumbers("1.6.20", "1.7.10") < 0);
assert(compareVersionNumbers("1.7.1", "1.7.10") < 0);
assert(compareVersionNumbers("1.7", "1.7.0") < 0);
assert(compareVersionNumbers("1.7", "1.8.0") < 0);

assert(compareVersionNumbers("1.7.10", "1.7.1") > 0);
assert(compareVersionNumbers("1.7.10", "1.6.1") > 0);
assert(compareVersionNumbers("1.7.10", "1.6.20") > 0);
assert(compareVersionNumbers("1.7.0", "1.7") > 0);
assert(compareVersionNumbers("1.8.0", "1.7") > 0);

assert(compareVersionNumbers("1.7.10", "1.7.10") === 0);
assert(compareVersionNumbers("1.7", "1.7") === 0);

assert(isNaN(compareVersionNumbers("1.7", "1..7")));
assert(isNaN(compareVersionNumbers("1.7", "Bad")));
assert(isNaN(compareVersionNumbers("1..7", "1.7")));
assert(isNaN(compareVersionNumbers("Bad", "1.7")));

alert("All done");
+2

Here is the implementation of the human species:

Array.prototype.humanSort = function() {
  return this.sort(function(a, b) {
    aa = a.split('.');
    bb = b.split('.');

    for(var x = 0; x < Math.max(aa.length, bb.length); x++) {
      if(aa[x] != bb[x]) {
        var cmp1 = (isNaN(parseInt(aa[x],10)))? aa[x] : parseInt(aa[x],10);
        var cmp2 = (isNaN(parseInt(bb[x],10)))? bb[x] : parseInt(bb[x],10);
        if(cmp1 == undefined || cmp2 == undefined)
          return aa.length - bb.length;
        else
          return (cmp1 < cmp2) ? -1 : 1;
      }
    }
    return 0;
  });
}

Name it as follows:

 ['1.12.5.4', '1.2.5.3'].humanSort(); // => ['1.2.5.3', '1.12.5.4']
0
source

Here, in my opinion, the “best” solution (because the proven one modifies the original array, and this is evil;)):

function latest(versions) {
    return versions.reduce(function(latest, current){
        var l = latest.split('.'),
            c = current.split('.');
        for (var i=0,len=Math.min(l.length, c.length); i<len; i++){
            if (+l[i] === +c[i]) {
                continue;
            } else {
                return +l[i] < +c[i] ? current : latest;
            }
        }
        return l.length < c.length ? current : latest;
    }, "0");
}

The results are exactly the same as the verified answer, except that the original array is still untouched.

[
  latest (['1.2.5.4', '1.3.5.3', '1.2.3.4.5', '1.24.2.1', '1.2.52']),
  latest (['1.2.5.4', '1.3.5.3', '1.2.3.4.5', '1.2.52']),
  latest (['1.2.5.4', '1.2.3.4.5', '1.2.52']),
  latest (['1.2.5.4', '1.2.3.4.5'])
]

/* Displays on JS console

    ["1.24.2.1", "1.3.5.3", "1.2.52", "1.2.5.4"]
*/

And it takes advantage of a function reducethat is designed specifically for this use.

0
source

All Articles