Sort version-dotted number strings in Javascript?

前端 未结 14 1696
栀梦
栀梦 2020-12-05 15:01

I have an array of following strings:

[\'5.5.1\', \'4.21.0\', \'4.22.0\', \'6.1.0\', \'5.1.0\', \'4.5.0\'] 

...etc.

I need a soluti

相关标签:
14条回答
  • 2020-12-05 15:29

    This seems to work provided there are only digits between the dots:

    var a = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0']
    
    a = a.map(function (x) {
        return x.split('.').map(function (x) {
            return parseInt(x)
        })
    }).sort(function (a, b) {
        var i = 0, m = a.length, n = b.length, o, d
        o = m < n ? n : m
        for (; i < o; ++i) {
            d = (a[i] || 0) - (b[i] || 0)
            if (d) return d
        }
        return 0
    }).map(function (x) {
        return x.join('.')
    })
    
    0 讨论(0)
  • 2020-12-05 15:33

    This can be in an easier way using the sort method without hardcoding any numbers and in a more generic way.

    enter code here
    
    var arr = ['5.1.2', '5.1.1', '5.1.1', '5.1.0', '5.7.2.2'];
    
    splitArray = arr.map(elements => elements.split('.'))
    
    //now lets sort based on the elements on the corresponding index of each array
    
    //mapped.sort(function(a, b) {
    //  if (a.value > b.value) {
    //    return 1;
    //  }
    //  if (a.value < b.value) {
    //    return -1;
    //  }
    //  return 0;
    //});
    
    //here we compare the first element with the first element of the next version number and that is [5.1.2,5.7.2] 5,5 and 1,7 and 2,2 are compared to identify the smaller version...In the end use the join() to get back the version numbers in the proper format.
    
    sortedArray = splitArray.sort((a, b) => {
      for (i in a) {
        if (parseInt(a[i]) < parseInt(b[i])) {
          return -1;
          break
        }
        if (parseInt(a[i]) > parseInt(b[i])) {
          return +1;
          break
        } else {
          continue
        }
      }
    }).map(p => p.join('.'))
    
    sortedArray = ["5.1.0", "5.1.1", "5.1.1", "5.1.2", "5.7.2.2"]

    0 讨论(0)
  • 2020-12-05 15:33
    • sort 1.0a notation correct
    • use native localeCompare to sort 1.090 notation

    function log(label,val){
      document.body.append(label,String(val).replace(/,/g," - "),document.createElement("BR"));
    }
    
    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","1.0a"];
    log(' input : ',v);
    log('sorted: ',sortVersions(v));
    log('no dups:',[...new Set(sortVersions(v))]);

    0 讨论(0)
  • 2020-12-05 15:36

    You could prepend all parts to fixed size strings, then sort that, and finally remove the padding again.

    var arr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
    arr = arr.map( a => a.split('.').map( n => +n+100000 ).join('.') ).sort()
             .map( a => a.split('.').map( n => +n-100000 ).join('.') );
    
    console.log(arr)

    Obviously you have to choose the size of the number 100000 wisely: it should have at least one more digit than your largest number part will ever have.

    With regular expression

    The same manipulation can be achieved without having to split & join, when you use the callback argument to the replace method:

    var arr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
    arr = arr.map( a => a.replace(/\d+/g, n => +n+100000 ) ).sort()
             .map( a => a.replace(/\d+/g, n => +n-100000 ) );
    
    console.log(arr)

    Defining the padding function once only

    As both the padding and its reverse functions are so similar, it seemed a nice exercise to use one function f for both, with an extra argument defining the "direction" (1=padding, -1=unpadding). This resulted in this quite obscure, and extreme code. Consider this just for fun, not for real use:

    var arr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
    arr = (f=>f(f(arr,1).sort(),-1)) ((arr,v)=>arr.map(a=>a.replace(/\d+/g,n=>+n+v*100000)));
    
    console.log(arr);

    Use the sort compare callback function

    You could use the compare function argument of sort to achieve the same:

    arr.sort( (a, b) => a.replace(/\d+/g, n => +n+100000 )
                         .localeCompare(b.replace(/\d+/g, n => +n+100000 )) );
    

    But for larger arrays this will lead to slower performance. This is because the sorting algorithm will often need to compare a certain value several times, each time with a different value from the array. This means that the padding will have to be executed multiple times for the same number. For this reason, it will be faster for larger arrays to first apply the padding in the whole array, then use the standard sort, and then remove the padding again.

    But for shorter arrays, this approach might still be the fastest. In that case, the so-called natural sort option -- that can be achieved with the extra arguments of localeCompare -- will be more efficient than the padding method:

    var arr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
    arr = arr.sort( (a, b) => a.localeCompare(b, undefined, { numeric:true }) );
    
    console.log(arr);

    More about the padding and unary plus

    To see how the padding works, look at the intermediate result it generates:

    [ "100005.100005.100001", "100004.100021.100000", "100004.100022.100000", 
      "100006.100001.100000", "100005.100001.100000" ]
    

    Concerning the expression +n+100000, note that the first + is the unary plus and is the most efficient way to convert a string-encoded decimal number to its numerical equivalent. The 100000 is added to make the number have a fixed number of digits. Of course, it could just as well be 200000 or 300000. Note that this addition does not change the order the numbers will have when they would be sorted numerically.

    The above is just one way to pad a string. See this Q&A for some other alternatives.

    0 讨论(0)
  • 2020-12-05 15:38
    'use strict';
    
    var arr = ['5.1.2', '5.1.1', '5.1.1', '5.1.0', '5.7.2.2'];
    
    Array.prototype.versionSort = function () {
    
        var arr = this;
    
        function isNexVersionBigger (v1, v2) {
            var a1 = v1.split('.');
            var b2 = v2.split('.');
            var len = a1.length > b2.length ? a1.length : b2.length;
            for (var k = 0; k < len; k++) {
                var a = a1[k] || 0;
                var b = b2[k] || 0;
                if (a === b) {
                    continue;
                } else
    
                    return b < a;
            }
        }
    
        for (var i = 0; i < arr.length; i++) {
            var min_i = i;
            for (var j = i + 1; j < arr.length; j++) {
                if (isNexVersionBigger(arr[i], arr[j])) {
                    min_i = j;
                }
            }
            var temp = arr[i];
            arr[i] = arr[min_i];
            arr[min_i] = temp;
        }
        return arr;
    }
    
    console.log(arr.versionSort());
    
    0 讨论(0)
  • 2020-12-05 15:39

    const arr = ["5.1.1","5.1.12","5.1.2","3.7.6","2.11.4","4.8.5","4.8.4","2.10.4"];
    const sorted = arr.sort((a,b) => {
      const ba = b.split('.');
      const d = a.split('.').map((a1,i)=>a1-ba[i]);
      return d[0] ? d[0] : d[1] ? d[1] : d[2]
    });
    
    console.log(sorted);

    0 讨论(0)
提交回复
热议问题