简体   繁体   English

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

[英]Sort version-dotted number strings in Javascript?

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. ...ETC。

I need a solution that will give me following ordered result我需要一个解决方案,它会给我以下命令的结果

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

I tried to implement a sort so it first sorts by the numbers in the first position, than in case of equality, sort by the numbers in the second position (after the first dot), and so on...我尝试实现一种排序,因此它首先按第一个 position 中的数字排序,而不是在相等的情况下,按第二个 position 中的数字(第一个点之后)排序,依此类推......

I tried using sort() and localeCompare() , but if I have elements '4.5.0' and '4.11.0' , I get them sorted as ['4.11.0','4.5.0'] , but I need to get ['4.5.0','4.11.0'] .我尝试使用sort()localeCompare() ,但如果我有元素'4.5.0''4.11.0' ,我将它们排序为['4.11.0','4.5.0'] ,但我需要得到['4.5.0','4.11.0']

How can I achieve this?我怎样才能做到这一点?

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.显然,您必须明智地选择数字 100000 的大小:它应该比您的最大数字部分至少多一个数字。

With regular expression带正则表达式

The same manipulation can be achieved without having to split & join, when you use the callback argument to the replace method:当您对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)

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).由于 padding 和它的反向函数都非常相似,对两者使用一个函数f似乎是一个很好的练习,并带有一个额外的参数来定义“方向”(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使用sort比较回调函数

You could use the compare function argument of sort to achieve the same:您可以使用sort的 compare 函数参数来实现相同的目的:

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:在这种情况下,所谓的自然排序选项——可以通过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);

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.关于表达式+n+100000 ,请注意第一个+一元加号,是将字符串编码的十进制数转换为其等效数字的最有效方法。 The 100000 is added to make the number have a fixed number of digits.添加 100000 以使数字具有固定位数。 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.当然,它也可以是 200000 或 300000。请注意,此添加不会更改数字在按数字排序时的顺序。

The above is just one way to pad a string.以上只是填充字符串的一种方法。 See this Q&A for some other alternatives.有关其他选择,请参阅此问答

If you are looking for a npm package to compare two semver version, https://www.npmjs.com/package/compare-versions is the one.如果您正在寻找一个 npm 包来比较两个semver版本, https ://www.npmjs.com/package/compare-versions 就是一个。

Then you can sort version like this:然后你可以像这样对版本进行排序:

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

You could split the strings and compare the parts.您可以拆分字符串并比较各个部分。

 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);<\/code><\/pre>
 .as-console-wrapper { max-height: 100% !important; top: 0; }<\/code><\/pre>

"

You can check in loop if values are different, return difference, else continue如果值不同,您可以在循环中检查,返回差异,否则继续

 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)

Though slightly late this would be my solution;虽然有点晚了,但这将是我的解决方案;

 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);<\/code><\/pre>

"

Here's a solution I developed based on @trincot's that will sort by semver even if the strings aren't exactly "1.2.3" - they could be ie "v1.2.3" or "2.4"这是我基于@trincot 开发的解决方案,即使字符串不完全是“1.2.3”,它也会按语义版本排序——它们可能是“v1.2.3”或“2.4”

 function sortSemVer(arr, reverse = false) { let semVerArr = arr.map(i => i.replace(/(\d+)/g, m => +m + 100000)).sort(); // +m is just a short way of converting the match to int if (reverse) semVerArr = semVerArr.reverse(); return semVerArr.map(i => i.replace(/(\d+)/g, m => +m - 100000)) } console.log(sortSemVer(["1.0.1", "1.0.9", "1.0.10"])) console.log(sortSemVer(["v2.1", "v2.0.9", "v2.0.12", "v2.2"], true))

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

This solution accounts for version numbers that might not be in the full, 3-part format (for example, if one of the version numbers is just 2 or 2.0 or 0.1, etc).此解决方案考虑了可能不是完整的 3 部分格式的版本号(例如,如果版本号之一只是 2 或 2.0 或 0.1 等)。

The custom sort function I wrote is probably mostly what you're looking for, it just needs an array of objects in the format {"major":X, "minor":X, "revision":X} :我编写的自定义排序函数可能主要是您正在寻找的,它只需要格式为{"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 (ie 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);

Inspired from the accepted answer, but ECMA5-compatible, and with regular string padding (see my comments on the answer):灵感来自已接受的答案,但与 ECMA5 兼容,并且具有常规字符串填充(请参阅我对答案的评论):

function sortCallback(a, b) {

    function padParts(version) {
        return version
            .split('.')
            .map(function (part) {
                return '00000000'.substr(0, 8 - part.length) + part;
            })
            .join('.');
    }

    a = padParts(a);
    b = padParts(b);

    return a.localeCompare(b);
}

Usage:用法:

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

 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);<\/code><\/pre>

"

  • sort 1.0a notation correct排序1.0a符号正确
  • use native localeCompare to sort 1.090 notation使用本机localeCompare1.090表示法进行排序

 function log(label,val){ document.body.append(label,String(val).replace(/,/g," - "),document.createElement("BR")); } const sortVersions = ( x, v = s => s.match(/[az]|\\d+/g).map(c => c==~~c ? String.fromCharCode(97 + c) : c) ) => x.sort((a, b) => (a + b).match(/[az]/) ? 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))]);

This can be in an easier way using the sort method without hardcoding any numbers and in a more generic way.这可以使用 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"]

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

In ES6 you can go without regex.在 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)]

If ES6 I do this:如果 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);
});
**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!!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM