简体   繁体   English

如何用数字对javascript中的字符串进行排序

[英]how to sort strings in javascript numerically

I would like to sort an array of strings (in javascript) such that groups of digits within the strings are compared as integers not strings.我想对字符串数组(在 javascript 中)进行排序,以便将字符串中的数字组作为整数而不是字符串进行比较。 I am not worried about signed or floating point numbers.我不担心有符号数或浮点数。

for example, the result should be ["a1b3","a9b2","a10b2","a10b11"] not ["a1b3","a10b11","a10b2","a9b2"]例如,结果应该是["a1b3","a9b2","a10b2","a10b11"]而不是["a1b3","a10b11","a10b2","a9b2"]

The easiest way to do this seems to be splitting each string on boundaries around groups of digits.最简单的方法似乎是在数字组周围的边界上拆分每个字符串。 Is there a pattern I can pass to String.split to split on character boundaries without removing any characters?是否有一种模式可以传递给 String.split 以在不删除任何字符的情况下拆分字符边界?

"abc11def22ghi".split(/?/) = ["abc","11","def","22","ghi"];

Or is there another way to compare strings that does not involve splitting them up, perhaps by padding all groups of digits with leading zeros so they are the same length?或者是否有另一种方法来比较字符串而不涉及将它们拆分,也许通过用前导零填充所有数字组使它们具有相同的长度?

"aa1bb" => "aa00000001bb", "aa10bb" => "aa00000010bb"

I am working with arbitrary strings, not strings that have a specific arrangement of digit groups.我正在使用任意字符串,而不是具有特定数字组排列的字符串。

Edit:编辑:

I like the /(\\d+)/ one liner from Gaby to split the array.我喜欢 Gaby 的/(\\d+)/一个衬垫来分割数组。 How backwards compatible is that?那是如何向后兼容的?

The solutions that parse the strings once in a way that can be used to rebuild the originals are much more efficient that this compare function.以可用于重建原始字符串的方式解析字符串一次的解决方案比此比较函数更有效。 None of the answers handle some strings starting with digits and others not, but that would be easy enough to remedy and was not explicit in the original question.没有一个答案处理一些以数字开头的字符串,而另一些则不处理,但这很容易补救,并且在原始问题中并不明确。

["a100","a20","a3","a3b","a3b100","a3b20","a3b3","!!","~~","9","10","9.5"].sort( function ( inA , inB ) {
    var                     result = 0;

    var                     a , b , pattern = /(\d+)/;
    var                     as = inA.split( pattern );
    var                     bs = inB.split( pattern );
    var                     index , count = as.length;

    if ( ( '' === as[0] ) === ( '' === bs[0] ) ) {
        if ( count > bs.length ) count = bs.length;

        for ( index = 0 ; index < count && 0 === result ; ++index ) {
            a = as[index]; b = bs[index];

            if ( index & 1 ) {
                result = a - b;
            } else {
                result = !( a < b ) ? ( a > b ) ? 1 : 0 : -1;
            }
        }

        if ( 0 === result ) result = as.length - bs.length;
    } else {
        result = !( inA < inB ) ? ( inA > inB ) ? 1 : 0 : -1;
    }

    return result;
} ).toString();

result: "!!,9,9.5,10,a3,a3b,a3b3,a3b20,a3b100,a20,a100,~~"结果: "!!,9,9.5,10,a3,a3b,a3b3,a3b20,a3b100,a20,a100,~~"

I think this does what you want我认为这可以满足您的要求

function sortArray(arr) {
    var tempArr = [], n;
    for (var i in arr) {
        tempArr[i] = arr[i].match(/([^0-9]+)|([0-9]+)/g);
        for (var j in tempArr[i]) {
            if( ! isNaN(n = parseInt(tempArr[i][j])) ){
                tempArr[i][j] = n;
            }
        }
    }
    tempArr.sort(function (x, y) {
        for (var i in x) {
            if (y.length < i || x[i] < y[i]) {
                return -1; // x is longer
            }
            if (x[i] > y[i]) {
                return 1;
            }
        }
        return 0;
    });
    for (var i in tempArr) {
        arr[i] = tempArr[i].join('');
    }
    return arr;
}
alert(
    sortArray(["a1b3", "a10b11", "a10b2", "a9b2"]).join(",")
);

Another variant is to use an instance of Intl.Collator with numeric option:另一种变体是使用带有数字选项的Intl.Collator实例:

 var array = ["a100","a20","a3","a3b","a3b100","a3b20","a3b3","!!","~~","9","10","9.5"]; var collator = new Intl.Collator([], {numeric: true}); array.sort((a, b) => collator.compare(a, b)); console.log(array);

Assuming what you want to do is just do a numeric sort by the digits in each array entry (ignoring the non-digits), you can use this:假设您想要做的只是按每个数组条目中的数字进行数字排序(忽略非数字),您可以使用:

function sortByDigits(array) {
    var re = /\D/g;

    array.sort(function(a, b) {
        return(parseInt(a.replace(re, ""), 10) - parseInt(b.replace(re, ""), 10));
    });
    return(array);
}

It uses a custom sort function that removes the digits and converts to a number each time it's asked to do a comparison.它使用自定义排序函数删除数字并在每次要求进行比较时转换为数字。 You can see it work here: http://jsfiddle.net/jfriend00/t87m2/ .你可以在这里看到它的工作: http : //jsfiddle.net/jfriend00/t87m2/

If this isn't what you want, then please clarify as your question is not very clear on how the sort should actually work.如果这不是您想要的,那么请澄清,因为您的问题不是很清楚排序应该如何实际工作。

Use this compare function for sorting ..使用此比较功能进行排序..

function compareLists(a,b){
    var alist = a.split(/(\d+)/), // split text on change from anything to digit and digit to anything
        blist = b.split(/(\d+)/); // split text on change from anything to digit and digit to anything

    alist.slice(-1) == '' ? alist.pop() : null; // remove the last element if empty
    blist.slice(-1) == '' ? blist.pop() : null; // remove the last element if empty

    for (var i = 0, len = alist.length; i < len;i++){
        if (alist[i] != blist[i]){ // find the first non-equal part
           if (alist[i].match(/\d/)) // if numeric
           {
              return +alist[i] - +blist[i]; // compare as number
           } else {
              return alist[i].localeCompare(blist[i]); // compare as string
           }
        }
    }

    return true;
}

Syntax句法

var data = ["a1b3","a10b11","b10b2","a9b2","a1b20","a1c4"];
data.sort( compareLists );
alert(data);

demo at http://jsfiddle.net/h9Rqr/7/演示在http://jsfiddle.net/h9Rqr/7/

Here's a more complete solution that sorts according to both letters and numbers in the strings这是一个更完整的解决方案,它根据字符串中的字母和数字进行排序

function sort(list) {
    var i, l, mi, ml, x;
    // copy the original array
    list = list.slice(0);

    // split the strings, converting numeric (integer) parts to integers
    // and leaving letters as strings
    for( i = 0, l = list.length; i < l; i++ ) {
        list[i] = list[i].match(/(\d+|[a-z]+)/g);
        for( mi = 0, ml = list[i].length; mi < ml ; mi++ ) {
            x = parseInt(list[i][mi], 10);
            list[i][mi] = !!x || x === 0 ? x : list[i][mi];
        }
    }

    // sort deeply, without comparing integers as strings
    list = list.sort(function(a, b) {
        var i = 0, l = a.length, res = 0;
        while( res === 0 && i < l) {
            if( a[i] !== b[i] ) {
                res = a[i] < b[i] ? -1 : 1;
                break;
            }

            // If you want to ignore the letters, and only sort by numbers
            // use this instead:
            // 
            // if( typeof a[i] === "number" && a[i] !== b[i] ) {
            //     res = a[i] < b[i] ? -1 : 1;
            //     break;
            // }

            i++;
        }
        return res;
    });

    // glue it together again
    for( i = 0, l = list.length; i < l; i++ ) {
        list[i] = list[i].join("");
    }
    return list;
}

I needed a way to take a mixed string and create a string that could be sorted elsewhere, so that numbers sorted numerically and letters alphabetically.我需要一种方法来获取混合字符串并创建一个可以在其他地方排序的字符串,以便数字按数字和字母顺序排序。 Based on answers above I created the following, which pads out all numbers in a way I can understand, wherever they appear in the string.根据上面的答案,我创建了以下内容,以我可以理解的方式填充所有数字,无论它们出现在字符串中的任何位置。

function padAllNumbers(strIn) {
    // Used to create mixed strings that sort numerically as well as non-numerically
    var patternDigits = /(\d+)/g; // This recognises digit/non-digit boundaries
    var astrIn = strIn.split( patternDigits ); // we create an array of alternating digit/non-digit groups

    var result = "";

    for (var i=0;i<astrIn.length;  i++) {
        if (astrIn[i] != "") { // first and last elements can be "" and we don't want these padded out
            if (isNaN(astrIn[i])) {
                result += astrIn[i];
            } else {
                result += padOneNumberString("000000000",astrIn[i]);
            }
        }
    }
    return result;
}

function padOneNumberString(pad,strNum,left) {
    // Pad out a string at left (or right)
    if (typeof strNum === "undefined") return pad;
    if (typeof left === "undefined") left = true;
    var padLen =  pad.length - (""+ strNum).length;
    var padding = pad.substr(0,padLen);
    return left?  padding + strNum : strNum + padding;
}

Sorting occurs from left to right unless you create a custom algorithm.除非您创建自定义算法,否则排序是从左到右进行的。 Letters or digits are compared digits first then letters.字母或数字先比较数字,然后比较字母。

However, what you want to accomplish as per your own example (a1, a9, a10) WON'T EVER HAPPEN.但是,根据您自己的示例 (a1, a9, a10) 想要完成的操作永远不会发生。 That would require you knowing the data before hand and spliting the string in every possible way before applying the sorting.这将要求您事先了解数据并在应用排序之前以各种可能的方式拆分字符串。

One final alternative would be:最后一种选择是:

a) break each and every string from left to right whenever is a change from letter to digit and vice versa; a) 每当字母和数字发生变化时,从左到右断开每个字符串,反之亦然; & b) then start the sorting on those groups from RIGHT-TO-LEFT. & b) 然后从右到左开始对这些组进行排序。 That will be a very demanding algorithm.这将是一个非常苛刻的算法。 Can be done!可以做到!

Finally, if you are the GENERATOR of the original "text", you should consider NORMALIZING the output where a1 a9 a10 could be outputed as a01 a09 a10.最后,如果您是原始“文本”的生成器,您应该考虑标准化输出,其中 a1 a9 a10 可以输出为 a01 a09 a10。 This way you could have full cotnrol of the final version of the algorithm.这样你就可以完全控制算法的最终版本。

Good luck!祝你好运!

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

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