在Javascript中对版本点数字符串进行排序?

时间:2016-10-23 09:41:41

标签: javascript sorting

我有一组以下字符串:

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

...等

我需要一个能让我获得有序结果的解决方案

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

我尝试实现排序,因此它首先按照第一个位置的数字进行排序,而不是在相等的情况下,按第二个位置(第一个点之后)的数字排序,依此类推......

我尝试使用 sort() localeCompare(),但如果我有元素'4.5.0''4.11.0',我会将它们排序为['4.11.0','4.5.0'],但我需要['4.5.0','4.11.0']

我怎样才能做到这一点?

16 个答案:

答案 0 :(得分:23)

您可以将所有部分添加到固定大小的字符串,然后对其进行排序,最后再次删除填充。

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)

显然你必须明智地选择数字100000的大小:它应该至少比你的最大数字部分多一个数字。

使用正则表达式

无需拆分即可实现相同的操作。当您使用replace方法的回调参数时加入:

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)

仅定义一次填充功能

由于填充及其反向功能都非常相似,因此对两者使用一个函数 f 似乎是一个很好的练习,并且额外的参数定义了“方向” “(1 =填充,-1 =取消填充)。这导致了这个非常模糊和极端的代码。考虑到这只是为了好玩,而不是实际使用:

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

使用sort比较回调函数

您可以使用sort的compare函数参数来实现相同的目的:

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

但是对于较大的阵列,这将导致性能降低。这是因为排序算法通常需要多次比较某个值,每次都与数组的值不同。这意味着对于相同的数字,必须多次执行填充。因此,较大的数组首先在整个数组中应用填充,然后使用标准排序,然后再次删除填充更快。

但对于较短的阵列,这种方法可能仍然是最快的。在这种情况下,所谓的自然排序选项 - 可以通过localeCompare的额外参数实现 - 将比填充方法更有效:

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

有关填充和一元加

的更多信息

要查看填充的工作原理,请查看它生成的中间结果:

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

关于表达式+n+100000,请注意第一个+unary plus,并且是将字符串编码的十进制数转换为其等效数字的最有效方法。添加100000以使该数字具有固定的位数。当然,它也可以是200000或300000.请注意,这些添加不会改变数字在数字排序时的顺序。

以上只是填充字符串的一种方法。有关其他选择,请参阅此Q&A

答案 1 :(得分:1)

您可以拆分字符串并比较各部分。

function customSort(data, order) {

    function isNumber(v) {
        return (+v).toString() === v;
    }

    var sort = {
            asc: function (a, b) {
                var i = 0,
                    l = Math.min(a.value.length, b.value.length);

                while (i < l && a.value[i] === b.value[i]) {
                    i++;
                }
                if (i === l) {
                    return a.value.length - b.value.length;
                }
                if (isNumber(a.value[i]) && isNumber(b.value[i])) {
                    return a.value[i] - b.value[i];
                }
                return a.value[i].localeCompare(b.value[i]);
            },
            desc: function (a, b) {
                return sort.asc(b, a);
            }
        },
        mapped = data.map(function (el, i) {
            return { index: i, value: el.split('') };
        });

    mapped.sort(sort[order] || sort.asc);
    return mapped.map(function (el) {
        return data[el.index];
    });
}

var array = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0'];

console.log('sorted array asc', customSort(array));
console.log('sorted array desc ', customSort(array, 'desc'));
console.log('original array ', array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 2 :(得分:1)

如果值不同,您可以检查循环,返回差异,否则继续

var a=['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];

a.sort(function(a,b){
  var a1 = a.split('.');
  var b1 = b.split('.');
  var len = Math.max(a1.length, b1.length);
  
  for(var i = 0; i< len; i++){
    var _a = +a1[i] || 0;
    var _b = +b1[i] || 0;
    if(_a === _b) continue;
    else return _a > _b ? 1 : -1
  }
  return 0;
})

console.log(a)

答案 3 :(得分:0)

如果点之间只有数字,这似乎有效:

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('.')
})

答案 4 :(得分:0)

虽然稍晚,这将是我的解决方案;

&#13;
&#13;
var 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"],
 sorted = arr.sort((a,b) => {var aa = a.split("."),
                                 ba = b.split(".");
                             return +aa[0] < +ba[0] ? -1
                                                    : aa[0] === ba[0] ? +aa[1] < +ba[1] ? -1
                                                                                        : aa[1] === ba[1] ? +aa[2] < +ba[2] ? -1
                                                                                                                            : 1
                                                                                                          : 1
                                                                      : 1;
                            });
 console.log(sorted);
&#13;
&#13;
&#13;

答案 5 :(得分:0)

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

答案 6 :(得分:0)

此解决方案考虑的版本号可能不是完整的3部分格式(例如,如果其中一个版本号仅为2或2.0或0.1等)。

我写的自定义排序函数可能主要是您正在寻找的,它只需要{"major":X, "minor":X, "revision":X}格式的对象数组:

var versionArr = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
var versionObjectArr = [];
var finalVersionArr = [];
/*
split each version number string by the '.' and separate them in an
object by part (major, minor, & revision).  If version number is not
already in full, 3-part format, -1 will represent that part of the
version number that didn't exist.  Push the object into an array that
can be sorted.
*/
for(var i = 0; i < versionArr.length; i++){
  var splitVersionNum = versionArr[i].split('.');
  var versionObj = {};
  switch(splitVersionNum.length){
    case 1:
      versionObj = {
        "major":parseInt(splitVersionNum[0]),
        "minor":-1,
        "revision":-1
      };
      break;
    case 2:
      versionObj = {
        "major":parseInt(splitVersionNum[0]),
        "minor":parseInt(splitVersionNum[1]),
        "revision":-1
      };
      break;
    case 3:
      versionObj = {
        "major":parseInt(splitVersionNum[0]),
        "minor":parseInt(splitVersionNum[1]),
        "revision":parseInt(splitVersionNum[2])
      };
  }
  versionObjectArr.push(versionObj);
}

//sort objects by parts, going from major to minor to revision number.
versionObjectArr.sort(function(a, b){
  if(a.major < b.major) return -1;
  else if(a.major > b.major) return 1;
  else {
    if(a.minor < b.minor) return -1;
    else if(a.minor > b.minor) return 1;
    else {
      if(a.revision < b.revision) return -1;
      else if(a.revision > b.revision) return 1;
    }
  }
});

/*
loops through sorted object array to recombine it's version keys to match the original string's value.  If any trailing parts of the version
number are less than 0 (i.e. they didn't exist so we replaced them with
-1) then leave that part of the version number string blank. 
*/
for(var i = 0; i < versionObjectArr.length; i++){
  var versionStr = "";
  for(var key in versionObjectArr[i]){
    versionStr = versionObjectArr[i].major;
    versionStr += (versionObjectArr[i].minor < 0 ? '' : "." + versionObjectArr[i].minor);
    versionStr += (versionObjectArr[i].revision < 0 ? '' : "." + versionObjectArr[i].revision);
  }
  finalVersionArr.push(versionStr);
}
console.log('Original Array: ',versionArr);
console.log('Expected Output: ',['4.5.0', '4.21.0', '4.22.0', '5.1.0', '5.5.1', '6.1.0']);
console.log('Actual Output: ', finalVersionArr);

答案 7 :(得分:0)

如果ES6我这样做:

versions.sort((v1, v2) => {
  let [, major1, minor1, revision1 = 0] = v1.match(/([0-9]+)\.([0-9]+)(?:\.([0-9]+))?/);
  let [, major2, minor2, revision2 = 0] = v2.match(/([0-9]+)\.([0-9]+)(?:\.([0-9]+))?/);
  if (major1 != major2) return parseInt(major1) - parseInt(major2);
  if (minor1 != minor2) return parseInt(minor1) - parseInt(major2);
  return parseInt(revision1) - parseInt(revision2);
});

答案 8 :(得分:0)

受到接受答案的启发,但ECMA5兼容,并且使用常规字符串填充(请参阅我对答案的评论):

['1.1', '1.0'].sort(sortCallback);

用法:

{{1}}

答案 9 :(得分:0)

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

答案 10 :(得分:0)

  • 正确排序1.0a表示法
  • 使用本机localeCompare1.090表示法进行排序

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

答案 11 :(得分:0)

可以使用sort方法以一种更简单的方式,而不用硬编码任何数字,并且以一种更通用的方式。

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

答案 12 :(得分:0)

const arr = ["5.2.0.1","5.2.1SW","5.2.1","6.1.0SW","6.1.0","6.2.0.1TSH","6.2.0.1SW","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);

答案 13 :(得分:0)

在 ES6 中,你可以不用正则表达式。

const versions = ["0.4", "0.11", "0.4.1", "0.4", "0.4.2", "2.0.1","2", "0.0.1", "0.2.3"];

const splitted = versions.map(version =>
    version
        .split('.')
        .map(i => +i))
        .map(i => {
           let items;
           if (i.length === 1) {
             items = [0, 0]
             i.push(...items)
           }
           if (i.length === 2) {
             items = [0]
             i.push(...items)
           }

           return i
        })
        .sort((a, b) => {
          for(i in a) {
            if (a[i] < b[i]) {
              return -1;
            }
            if (a[i] > b[i]) {
              return +1;
            }
        }
    })
    .map(item => item.join('.'))

const sorted = [...new Set(splitted)]

答案 14 :(得分:-1)

**Sorted Array Object by dotted version value**

    var sampleData = [
      { name: 'Edward', value: '2.1.2' },
      { name: 'Sharpe', value: '2.1.3' },
      { name: 'And', value: '2.2.1' },
      { name: 'The', value: '2.1' },
      { name: 'Magnetic', value: '2.2' },
      { name: 'Zeros', value: '0' },
      { name: 'Zeros', value: '1' }
    ];
    arr = sampleData.map( a => a.value).sort();
    var requireData = [];

    arr.forEach(function(record, index){    
      var findRecord = sampleData.find(arr => arr.value === record);
        if(findRecord){
          requireData.push(findRecord);
        }
      });
    console.log(requireData);

    [check on jsfiddle.net][1]
    [1]: https://jsfiddle.net/jx3buswq/2/

    It is corrected now!!!

答案 15 :(得分:-1)

如果您正在寻找一个npm软件包来比较两个semver版本,则https://www.npmjs.com/package/compare-versions是一个版本。

然后您可以比较这样的版本:

// ES6/TypeScript
import compareVersions from 'compare-versions';

compareVersions('10.1.8', '10.0.4'); //  1
compareVersions('10.0.1', '10.0.1'); //  0
compareVersions('10.1.1', '10.2.2'); // -1