【问题标题】:Sort Array Elements (string with numbers), natural sort对数组元素进行排序(带数字的字符串),自然排序
【发布时间】:2013-03-06 21:57:53
【问题描述】:

我有一个类似的数组;

["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"]

并且需要对其进行排序,使其看起来像;

["IL0 Foo", "IL3 Bob says hello", "IL10 Baz", "PI0 Bar"]

我试过排序功能;

function compare(a,b) {
  if (a < b)
     return -1;
  if (a > b)
    return 1;
  return 0;
}

但这给出了命令

["IL0 Foo", "IL10 Baz", "IL3 Bob says hello", "PI0 Bar"]

我试图想出一个可行但无法理解的正则表达式。
如果有帮助,格式将始终是 2 个字母、x 个数字,然后是任意数量的字符。

【问题讨论】:

  • 先字母,后数字?
  • @BradChristie,是的,按前两个字母排序,然后是数字,其他字符不需要(但会很好)
  • 那么如果你有IL10 HelloIL10 Bob,哪个先出现?
  • 理想世界中的 IL10 Bob
  • @RyanGates 但这不是按字母顺序排列的,与 DOM 中的元素无关。

标签: javascript jquery sorting


【解决方案1】:

您可以将String#localeCompareoptions 一起使用

灵敏度

字符串中的哪些差异应导致非零结果值。可能的值是:

  • "base":只有基本字母不同的字符串比较不相等。示例:a ≠ ba = áa = A
  • "accent":只有在基本字母或重音和其他变音符号不同的字符串比较为不相等。示例:a ≠ ba ≠ áa = A
  • "case":只有基本字母或大小写不同的字符串比较不相等。示例:a ≠ ba = áa ≠ A
  • "variant":基本字母、重音符号和其他变音符号不同的字符串,或者大小写比较不相等。也可以考虑其他差异。示例:a ≠ ba ≠ áa ≠ A

使用“排序”的默认值为“变体”;它依赖于使用“搜索”的语言环境。

数字

是否应使用数字排序规则,例如“1”true 和false;默认为false。可以通过选项属性或通过 Unicode 扩展键设置此选项;如果两者都提供,options 属性优先。实现不需要支持此属性。

var array = ["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"];

array.sort(function (a,b) {
    return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
});

console.log(array);

【讨论】:

    【解决方案2】:

    用前导零填充字符串中的数字,然后正常排序。

    var naturalSort = function (a, b) {
        a = ('' + a).replace(/(\d+)/g, function (n) { return ('0000' + n).slice(-5) });
        b = ('' + b).replace(/(\d+)/g, function (n) { return ('0000' + n).slice(-5) });
        return a.localeCompare(b);
    }
    
    var naturalSortModern = function (a, b) {
        return ('' + a).localeCompare(('' + b), 'en', { numeric: true });
    }
    
    console.dir((["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"].sort(naturalSort)));
    
    console.dir((["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"].sort(naturalSortModern)));

    【讨论】:

      【解决方案3】:

      我非常喜欢 georg 的解决方案,但我需要下划线(“_”)在数字之前进行排序。以下是我修改他的代码的方式:

      var chunkRgx = /(_+)|([0-9]+)|([^0-9_]+)/g;
      function naturalCompare(a, b) {
          var ax = [], bx = [];
          
          a.replace(chunkRgx, function(_, $1, $2, $3) {
              ax.push([$1 || "0", $2 || Infinity, $3 || ""])
          });
          b.replace(chunkRgx, function(_, $1, $2, $3) {
              bx.push([$1 || "0", $2 || Infinity, $3 || ""])
          });
          
          while(ax.length && bx.length) {
              var an = ax.shift();
              var bn = bx.shift();
              var nn = an[0].localeCompare(bn[0]) || 
                       (an[1] - bn[1]) || 
                       an[2].localeCompare(bn[2]);
              if(nn) return nn;
          }
          
          return ax.length - bx.length;
      }
      
      /////////////////////////
      
      test = [
          "img12.png",
          "img10.png",
          "img2.png",
          "img1.png",
          "img101.png",
          "img101a.png",
          "abc10.jpg",
          "abc10",
          "abc2.jpg",
          "20.jpg",
          "20",
          "abc",
          "abc2",
          "_abc",
          "_ab_c",
          "_ab__c",
          "_abc_d",
          "ab_",
          "abc_",
          "_ab_cd",
          ""
      ];
      
      test.sort(naturalCompare)
      document.write("<pre>" + JSON.stringify(test,0,3));

      【讨论】:

        【解决方案4】:

        这称为“自然排序”,可以在 JS 中这样实现:

        function naturalCompare(a, b) {
            var ax = [], bx = [];
        
            a.replace(/(\d+)|(\D+)/g, function(_, $1, $2) { ax.push([$1 || Infinity, $2 || ""]) });
            b.replace(/(\d+)|(\D+)/g, function(_, $1, $2) { bx.push([$1 || Infinity, $2 || ""]) });
            
            while(ax.length && bx.length) {
                var an = ax.shift();
                var bn = bx.shift();
                var nn = (an[0] - bn[0]) || an[1].localeCompare(bn[1]);
                if(nn) return nn;
            }
        
            return ax.length - bx.length;
        }
        
        /////////////////////////
        
        test = [
            "img12.png",
            "img10.png",
            "img2.png",
            "img1.png",
            "img101.png",
            "img101a.png",
            "abc10.jpg",
            "abc10",
            "abc2.jpg",
            "20.jpg",
            "20",
            "abc",
            "abc2",
            ""
        ];
        
        test.sort(naturalCompare)
        document.write("<pre>" + JSON.stringify(test,0,3));

        要按相反顺序排序,只需交换参数即可:

        test.sort(function(a, b) { return naturalCompare(b, a) })
        

        或者干脆

        test = test.sort(naturalCompare).reverse();
        

        【讨论】:

        • 似乎更喜欢以字母字符开头的字符串而不是数字字符。通过将$1 || 0 替换为typeof($1) == 'undefined' ? Infinity : $1 来修复
        • 谁能帮助如何实现数组的升序和降序排序?
        • 感谢 Georg 的精彩回答!
        • 请注意.reverse 将再次迭代数组。
        • 有人能详细解释一下正则表达式替换/Infinity 发生了什么吗?那么带有移位机制的while循环呢?我不完全明白那里发生了什么
        【解决方案5】:

        添加另一种选择(为什么不):

        var ary = ["IL0 Foo", "PI0 Bar", "IL10 Hello", "IL10 Baz", "IL3 Bob says hello"];
        
        // break out the three components in to an array
        // "IL10 Bar" => ['IL', 10, 'Bar']
        function getParts(i){
            i = i || '';
            var parts = i.match(/^([a-z]+)([0-9]+)(\s.*)$/i);
            if (parts){
                return [
                    parts[1],
                    parseInt(parts[2], 10),
                    parts[3]
                ];
            }
            return []; // erroneous
        }
        ary.sort(function(a,b){
            // grab the parts
            var _a = getParts(a),
                _b = getParts(b);
        
            // trouble parsing (both fail = no shift, otherwise
            // move the troubles element to end of the array)
            if(_a.length == 0 && _b.length == 0) return 0;
            if(_a.length == 0) return -1;
            if(_b.length == 0) return 1;
        
            // Compare letter portion
            if (_a[0] < _b[0]) return -1;
            if (_a[0] > _b[0]) return 1;
            // letters are equal, continue...
        
            // compare number portion
            if (_a[1] < _b[1]) return -1;
            if (_a[1] > _b[1]) return 1;
            // numbers are equal, continue...
        
            // compare remaining string
            if (_a[2] < _b[2]) return -1;
            if (_a[2] > _b[2]) return 1;
            // strings are equal, continue...
        
            // exact match
            return 0;
        });
        

        jsfiddle example

        【讨论】:

          【解决方案6】:

          不漂亮,但请检查前两个字符代码。如果全部相等,则解析并比较数字:

          var arr = ["IL0 Foo", "IL10 Baz", "IL3 Bob says hello", "PI0 Bar"];
          arr.sort(function (a1, b1) {
              var a = parseInt(a1.match(/\d+/g)[0], 10),
                  b = parseInt(b1.match(/\d+/g)[0], 10),
                  letterA = a1.charCodeAt(0),
                  letterB = b1.charCodeAt(0),
                  letterA1 = a1.charCodeAt(1),
                  letterB1 = b1.charCodeAt(1);
              if (letterA > letterB) {
                  return 1;
              } else if (letterB > letterA) {
                  return -1;
              } else {
                  if (letterA1 > letterB1) {
                      return 1;
                  } else if (letterB1 > letterA1) {
                      return -1;
                  }
                  if (a < b) return -1;
                  if (a > b) return 1;
                  return 0;
              }
          });
          

          Example

          【讨论】:

          • 这给了我命令 ["IL0 Foo"、"IL10 Baz"、"IL3 Bob 说你好"、"PI0 Bar"]。 IL3 应该是第二个元素
          【解决方案7】:

          您可以执行这样的正则表达式来获取字符串的非数字和数字部分:

          var s = "foo124bar23";
          s.match(/[^\d]+|\d+/g)
          

          返回:["foo", "124" , "bar" , "23"]

          然后在您的比较函数中,您可以遍历两个字符串的各个部分,逐部分比较它们。第一个不匹配的部分决定了整体比较的结果。对于每个部分,检查该部分是否以数字开头,如果是,则在进行比较之前将其解析为数字。

          【讨论】:

          • 谢谢你:),你在编程方面非常出色@Tim Goodman
          【解决方案8】:
          var re = /([a-z]+)(\d+)(.+)/i;
          var arr = ["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"];
          var order = arr.sort( function(a,b){
              var ma = a.match(re),
                  mb = b.match(re),
                  a_str = ma[1],
                  b_str = mb[1],
                  a_num = parseInt(ma[2],10),
                  b_num = parseInt(mb[2],10),
                  a_rem = ma[3],
                  b_rem = mb[3];
              return a_str > b_str ? 1 : a_str < b_str ? -1 : a_num > b_num ? 1 : a_num < b_num ? -1 : a_rem > b_rem;  
          });
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-12-03
            • 2015-04-06
            • 1970-01-01
            • 2016-07-18
            • 2015-06-07
            相关资源
            最近更新 更多