【问题标题】:Javascript : natural sort of alphanumerical stringsJavascript:自然排序的字母数字字符串
【发布时间】:2011-02-17 15:25:47
【问题描述】:

我正在寻找对包含数字和文本以及它们的组合的数组进行排序的最简单方法。

例如

'123asd'
'19asd'
'12345asd'
'asd123'
'asd12'

变成

'19asd'
'123asd'
'12345asd'
'asd12'
'asd123'

这将与another question I've asked here的解决方案结合使用。

排序函数本身可以工作,我需要的是一个可以说'19asd'小于'123asd'的函数。

我正在用 JavaScript 写这个。

编辑:正如 adormitu 所指出的,我正在寻找的是一个自然排序的函数

【问题讨论】:

标签: javascript sorting natural-sort


【解决方案1】:

这现在可以在使用 localeCompare 的现代浏览器中实现。通过传递numeric: true 选项,它将智能地识别数字。您可以使用sensitivity: 'base' 不区分大小写。在 Chrome、Firefox 和 IE11 中测试。

这是一个例子。它返回1,意思是10在2之后:

'10'.localeCompare('2', undefined, {numeric: true, sensitivity: 'base'})

对于对大量字符串进行排序时的性能,文章说:

当比较大量的字符串时,例如在对大型数组进行排序时,最好创建一个 Intl.Collat​​or 对象并使用其 compare 属性提供的函数。 Docs link

var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
var myArray = ['1_Document', '11_Document', '2_Document'];
console.log(myArray.sort(collator.compare));

【讨论】:

  • 如果要对对象数组进行排序,也可以使用Collat​​or:codepen.io/TimPietrusky/pen/rKzoGN
  • 为了澄清上面的评论:“如果没有提供或未定义 locales 参数,则使用运行时的默认语言环境。”
  • @frodo2975 这里的未定义参数是什么。目的是什么?
  • @Jayden 我们传递 undefined 以避免必须指定区域设置,它将使用浏览器的默认区域设置。
  • @frodo2975 明白了..非常感谢!!
【解决方案2】:

如果你有一个对象数组,你可以这样做:

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

var myArrayObjects = [{
    "id": 1,
    "name": "1 example"
  },
  {
    "id": 2,
    "name": "100 example"
  },
  {
    "id": 3,
    "name": "12 example"
  },
  {
    "id": 4,
    "name": "5 example"
  },

]

myArrayObjects = myArrayObjects.sort(function(a, b) {
  return a.name.localeCompare(b.name, undefined, {
    numeric: true,
    sensitivity: 'base'
  });
});
console.log(myArrayObjects);

【讨论】:

    【解决方案3】:

    要比较值,您可以使用比较方法-

    function naturalSorter(as, bs){
        var a, b, a1, b1, i= 0, n, L,
        rx=/(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.\D+)|(\.$)/g;
        if(as=== bs) return 0;
        a= as.toLowerCase().match(rx);
        b= bs.toLowerCase().match(rx);
        L= a.length;
        while(i<L){
            if(!b[i]) return 1;
            a1= a[i],
            b1= b[i++];
            if(a1!== b1){
                n= a1-b1;
                if(!isNaN(n)) return n;
                return a1>b1? 1:-1;
            }
        }
        return b[i]? -1:0;
    }
    

    但是为了加快排序数组的速度,请在排序之前对数组进行装配, 所以你只需要做小写转换和正则表达式 一次,而不是在排序的每一步。

    function naturalSort(ar, index){
        var L= ar.length, i, who, next, 
        isi= typeof index== 'number', 
        rx=  /(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.(\D+|$))/g;
        function nSort(aa, bb){
            var a= aa[0], b= bb[0], a1, b1, i= 0, n, L= a.length;
            while(i<L){
                if(!b[i]) return 1;
                a1= a[i];
                b1= b[i++];
                if(a1!== b1){
                    n= a1-b1;
                    if(!isNaN(n)) return n;
                    return a1>b1? 1: -1;
                }
            }
            return b[i]!= undefined? -1: 0;
        }
        for(i= 0; i<L; i++){
            who= ar[i];
            next= isi? ar[i][index] || '': who;
            ar[i]= [String(next).toLowerCase().match(rx), who];
        }
        ar.sort(nSort);
        for(i= 0; i<L; i++){
            ar[i]= ar[i][1];
        }
    }
    

    【讨论】:

    • 在我的情况下这是否可行,内部数组决定外部数组的顺序?
    • String.prototype.tlc() 是什么?这是你自己的代码还是你从某个地方得到的?如果是后者,请链接到页面。
    • 抱歉错误已更正,谢谢。如果要 a[1] 和 b[1] 控制排序,请使用 a= String(a[1]).toLowerCase(); b= String(b[1]).toLowerCase();
    • 我刚刚有一个我想要排序的数据列表,认为在 Chrome 开发工具控制台中应该很容易做到 - 感谢该功能!
    【解决方案4】:

    想象一个数字零填充函数n =&gt; n.padStart(8, "0"),它接受任何数字并对其进行填充,即

    • “19”->“00000019”
    • “123”->“00000123”

    此函数可用于帮助对"19" 字符串进行排序,使其出现在"123" 字符串之前。

    让我们添加一个正则表达式/\d+/g 创建自然扩展函数str =&gt; str.replace(/\d+/g, n =&gt; n.padStart(8, "0")),它只查找字符串中的数字部分并填充它们,即

    • “19asd”->“00000019asd”
    • “123asd”->“00000123asd”

    现在,我们可以使用这个自然展开函数来帮助实现自然顺序排序:

    const list = [
        "123asd",
        "19asd",
        "12345asd",
        "asd123",
        "asd12"
    ];
    
    const ne = str => str.replace(/\d+/g, n => n.padStart(8, "0"));
    const nc = (a,b) => ne(a).localeCompare(ne(b));
    
    console.log(list.map(ne).sort()); // intermediate values
    console.log(list.sort(nc); // result

    list.map(ne).sort() 演示的中间结果显示了ne 自然扩展函数的作用。它仅在字符串的数字部分实现数字零填充,并保持字母组件不变。

    [
      "00000019asd",
      "00000123asd",
      "00012345asd",
      "asd00000012",
      "asd00000123"
    ]
    

    解决方案的最终版本实现了一个自然顺序比较器 nc 实现为 (a,b) =&gt; ne(a).localeCompare(ne(b)) 并在 list.sort(nc) 中使用它,因此事情得到正确排序:

    [
      "19asd",
      "123asd",
      "12345asd",
      "asd12",
      "asd123"
    ]
    

    【讨论】:

      【解决方案5】:

      截至 2019 年处理此问题的功能最齐全的库似乎是 natural-orderby

      import { orderBy } from 'natural-orderby'
      
      const unordered = [
        '123asd',
        '19asd',
        '12345asd',
        'asd123',
        'asd12'
      ]
      
      const ordered = orderBy(unordered)
      
      // [ '19asd',
      //   '123asd',
      //   '12345asd',
      //   'asd12',
      //   'asd123' ]
      

      它不仅接受字符串数组,还可以按对象数组中某个键的值进行排序。它还可以自动识别和排序以下字符串:货币、日期、货币和一堆其他东西。

      令人惊讶的是,gzip 压缩后也只有 1.6kB。

      【讨论】:

      • 虽然没有明确说明,但您的答案似乎特定于 Node.JS。
      • @StephenQuan 谢谢-我更新了使用 ES6 模块语法的答案,这不是 NodeJS 特定的。
      【解决方案6】:

      以@Adrien Be 的上述回答为基础,并使用Brian HuismanDavid koelle 创建的代码,这里是一个对象数组的修改原型排序:

      //Usage: unsortedArrayOfObjects.alphaNumObjectSort("name");
      //Test Case: var unsortedArrayOfObjects = [{name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a10"}, {name: "a5"}, {name: "a13"}, {name: "a20"}, {name: "a8"}, {name: "8b7uaf5q11"}];
      //Sorted: [{name: "8b7uaf5q11"}, {name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a5"}, {name: "a8"}, {name: "a10"}, {name: "a13"}, {name: "a20"}]
      
      // **Sorts in place**
      Array.prototype.alphaNumObjectSort = function(attribute, caseInsensitive) {
        for (var z = 0, t; t = this[z]; z++) {
          this[z].sortArray = new Array();
          var x = 0, y = -1, n = 0, i, j;
      
          while (i = (j = t[attribute].charAt(x++)).charCodeAt(0)) {
            var m = (i == 46 || (i >=48 && i <= 57));
            if (m !== n) {
              this[z].sortArray[++y] = "";
              n = m;
            }
            this[z].sortArray[y] += j;
          }
        }
      
        this.sort(function(a, b) {
          for (var x = 0, aa, bb; (aa = a.sortArray[x]) && (bb = b.sortArray[x]); x++) {
            if (caseInsensitive) {
              aa = aa.toLowerCase();
              bb = bb.toLowerCase();
            }
            if (aa !== bb) {
              var c = Number(aa), d = Number(bb);
              if (c == aa && d == bb) {
                return c - d;
              } else {
                return (aa > bb) ? 1 : -1;
              }
            }
          }
      
          return a.sortArray.length - b.sortArray.length;
        });
      
        for (var z = 0; z < this.length; z++) {
          // Here we're deleting the unused "sortArray" instead of joining the string parts
          delete this[z]["sortArray"];
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2021-12-20
        • 2010-11-23
        • 1970-01-01
        • 1970-01-01
        • 2021-11-28
        • 1970-01-01
        相关资源
        最近更新 更多