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

IT技术 javascript sorting
2021-01-19 11:24:05

我有以下字符串数组:

['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'and '4.11.0',我会将它们排序为['4.11.0','4.5.0'],但我需要将['4.5.0','4.11.0'].

我怎样才能做到这一点?

6个回答

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

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=padding,-1=unpadding)。这导致了这个非常晦涩且极端的代码。考虑这只是为了好玩,而不是为了实际使用:

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来实现相同的目的:

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,请注意,第一个+一元加号,是将字符串编码的十进制数转换为其等效数字的最有效方法。添加 100000 使号码具有固定位数。当然,它也可以是 200000 或 300000。请注意,此添加不会更改数字按数字排序时的顺序。

以上只是填充字符串的一种方法。有关其他一些替代方案,请参阅此问答

还发现这种方法在['1.1', '1.01']
2021-03-15 11:24:05
啊啊,IE 缺少 String.prototype.repeat() ……因此,您必须改为: '00000000'.substr(0, 8 - part.length) + part
2021-04-03 11:24:05
是的,这种情况很棘手,无论是解释还是以编程方式解析。这可能就是“语义版本控制”禁止它的原因。尽管如此,如果需要支持它,我已经发布了一个支持它的函数
2021-04-06 11:24:05
@GrasDouble,这['1.1', '1.01']是一个有趣的案例。这可能会让人感到困惑:['4.21.0', '4.5.0', '4.13.0', '4.06.0']...至少有两种方法可以为此解释升序:(1)通过数值:['4.5.0', '4.06.0', '4.13.0', '4.21.0'],或(2)通过字符比较:['4.06.0', '4.13.0', '4.21.0', '4.5.0']并且在 OP 的示例中 4.21 预计会更高比4.5,这是根据逻辑(1)。我想人们必须选择应该通过哪种逻辑(1 或 2)来生成订单。
2021-04-10 11:24:05
而不是+part + 100000,我做了一个常规的字符串填充:'0'.repeat(8 - part.length) + part这样你也支持像“1.0a”这样的版本。
2021-04-14 11:24:05

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

然后你可以像这样对版本进行排序:

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

var versions = ['5.5.1', '4.21.0', '4.22.0', '6.1.0', '5.1.0', '4.5.0'];
var sorted = versions.sort(compareVersions);

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

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

这是正确的解决方案。特林科特的回答有点老套。
2021-04-06 11:24:05

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

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)

不是 Math.max,因为那样你就用完了较短的列表。使用 min 和 thenreturn b1.length - a1.length而不是零。
2021-04-12 11:24:05

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

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