【问题标题】:how to sort strings in javascript numerically如何在javascript中对字符串进行数字排序
【发布时间】:2011-12-27 18:41:00
【问题描述】:

我想对字符串数组(在 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,~~"

【问题讨论】:

  • 非数字部分是否始终相同?如果不是,排序算法是否应该按 ASCII 顺序对它们进行排序?
  • 在您的示例中,提取的是 13、92、102、1011?还是更像 1.3、9.2、10.2、10.11?我的意思是第一个数字更重要还是忽略了字母?
  • ...哦,您还想对非整数进行排序,我现在明白了...

标签: javascript regex sorting split natural-sort


【解决方案1】:

另一个变体是使用带有数字选项的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);

【讨论】:

  • 这对我来说效果很好。谢谢!
【解决方案2】:

假设你想要做的只是对每个数组条目中的数字进行数字排序(忽略非数字),你可以使用这个:

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/

如果这不是您想要的,那么请澄清一下,因为您的问题不是很清楚排序应该如何实际工作。

【讨论】:

  • 我认为如果遇到零前导编号可能会出现问题,不是吗? IE。 abc03def45
  • @Dr.Dredel - 使用 parseInt 使其成为纯数字排序。当转换为应有的真实数字时,前导零将被忽略。我没有发现任何问题。
  • 我认为 OP 仍然想对非数字进行排序。
  • @LeeKowalkowski - 这是一个相当不清楚的问题,OP 没有澄清。如果我的答案不是他们想要的,我已经要求 OP 做出回应并澄清,但他们没有。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    使用此比较功能进行排序..

    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/

    【讨论】:

      【解决方案5】:

      我认为这是你想要的

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

      【讨论】:

      • 与堆栈排序一起使用。
      • 如果某些字符串以数字开头,而其他字符串以字母开头,则不起作用。编辑已提交。
      【解决方案6】:

      Here's a more complete solution 根据字符串中的字母和数字进行排序

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

      【讨论】:

      • 我认为 OP 想忽略非数字,只按数字排序。
      • @jfriend00:嗯……你可能是对的。如果是这样,您可以在比较函数的while-loop 中添加一个typeof a[i] === "number" 子句
      【解决方案7】:

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

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

      最后一种选择是:

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

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

      祝你好运!

      【讨论】:

        猜你喜欢
        • 2010-09-08
        • 2017-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-19
        相关资源
        最近更新 更多