如何在 JavaScript 中对字符串进行数字排序

发布于 2024-12-15 04:45:38 字数 1637 浏览 3 评论 0原文

我想对字符串数组(在 JavaScript 中)进行排序,以便将字符串中的数字组作为整数而不是字符串进行比较。我不担心有符号或浮点数。

例如,结果应为 ["a1b3","a9b2","a10b2","a10b11"] 而不是 ["a1b3","a10b11","a10b2","a9b2 "]

最简单的方法似乎是在数字组周围的边界上分割每个字符串。是否有一种模式可以传递给 String.split 以在字符边界上分割而不删除任何字符?

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

或者是否有另一种方法来比较字符串不涉及将它们分开,也许通过用前导零填充所有数字组以使它们具有相同的长度?

<代码>“aa1bb”=> “aa00000001bb”,“aa10bb”=> "aa00000010bb"

我正在处理任意字符串,而不是具有特定数字组排列的字符串。


我喜欢 Gaby 的 /(\d+)/ 一行来分割数组。向后兼容程度如何?

以可用于重建原始字符串的方式解析一次字符串的解决方案比此比较函数要高效得多。没有一个答案可以处理某些以数字开头的字符串,而另一些则不能,但这很容易补救,并且在原始问题中并不明确。

["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();

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

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

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?

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


I like the /(\d+)/ one liner from Gaby to split the array. 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,~~"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

纸伞微斜 2024-12-22 04:45:38

另一种变体是使用 的实例Intl.Collat​​or 带有数字选项:

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

Another variant is to use an instance of Intl.Collator with the numeric option:

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

森末i 2024-12-22 04:45:38

我认为这符合你的要求

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(",")
);

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(",")
);
那小子欠揍 2024-12-22 04:45:38

假设您只想按每个数组条目中的数字进行数字排序(忽略非数字),您可以使用以下方法:

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

它使用自定义排序函数,每次要求执行此操作时都会删除数字并转换为数字一个比较。您可以在这里看到它的工作原理:http://jsfiddle.net/jfriend00/t87m2/

Assuming you want to 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/.

神妖 2024-12-22 04:45:38

使用此比较函数进行排序...

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

语法

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

有一个演示 http://jsfiddle.net/h9Rqr/ 7/

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

There is a demo at http://jsfiddle.net/h9Rqr/7/.

羁客 2024-12-22 04:45:38

这是一个更完整的解决方案,它根据字符串中的字母和数字进行排序

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

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;
}
冧九 2024-12-22 04:45:38

我需要一种方法来获取混合字符串并创建一个可以在其他地方排序的字符串,以便数字按数字排序,字母按字母顺序排序。根据上面的答案,我创建了以下内容,它以我可以理解的方式填充所有数字,无论它们出现在字符串中的何处。

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

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;
}
望笑 2024-12-22 04:45:38

排序从左到右进行,除非您创建自定义算法。字母或数字先比较数字,然后比较字母。

但是,您想要按照自己的示例完成的任务(a1、a9、a10)永远不会发生。这需要您事先了解数据并在应用排序之前以各种可能的方式拆分字符串。

最后一个替代方案是:

a)每当从字母到数字发生变化时,从左到右断开每个字符串,反之亦然; &
b) 然后开始从从右到左对这些组进行排序。这将是一个非常苛刻的算法。可以做到!

最后,如果您是原始“文本”的生成器,您应该考虑对输出进行标准化,其中 a1 a9 a10 可以输出为 a01 a09 a10。这样您就可以完全控制算法的最终版本。

Sorting occurs from left to right unless you create a custom algorithm. Letters or digits are compared digits first and then letters.

However, what you want to accomplish as per your own example (a1, a9, a10) won’t ever happen. That would require you knowing the data beforehand and splitting 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 there is a change from letter to digit and vice versa; &
b) then start the sorting on those groups from right-to-left. 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 outputted as a01 a09 a10. This way you could have full control of the final version of the algorithm.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文