Javascript:自然排序的字母数字字符串

Posted

技术标签:

【中文标题】Javascript:自然排序的字母数字字符串【英文标题】:Javascript : natural sort of alphanumerical strings 【发布时间】:2022-01-20 09:26:39 【问题描述】:

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

例如

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

变成

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

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

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

我正在用 javascript 写这个。

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

【问题讨论】:

另见How do you do string comparison in JavaScript?***.com/questions/51165/… 最初的问题是在 2010 年提出的,所以也就不足为奇了 :) How to sort strings in JavaScript的可能重复 【参考方案1】:

所以你需要一个自然排序

如果是这样,那么this script by Brian Huisman based on David koelle's work 可能就是您所需要的。

Brian Huisman 的解决方案现在似乎直接托管在 David Koelle 的博客上:

Brian Huisman's javascript solutions David koelle's article on the subject

【讨论】:

正确,自然的排序是我正在寻找的。我会查看您发送的链接,谢谢 这是一种非常不自然的排序。它不会产生字母排序。 @tchrist:“它不会产生字母排序”是什么意思? 它工作正常,但不能正确处理负数。即:它将产生['-1'。 '-2', '0', '1', '2']. @mhitza 这段代码似乎做得很好github.com/litejs/natural-compare-lite 快​​速测试jsbin.com/bevututodavi/1/edit?js,console【参考方案2】:

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

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 开发工具控制台中应该很容易做到 - 感谢该功能!【参考方案3】:

这现在可以在使用 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 参数,则使用运行时的默认语言环境。”【参考方案4】:

以@Adrien Be 的上述回答为基础,并使用Brian Huisman 和David 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"];
  

【讨论】:

【参考方案5】:

想象一个数字零填充函数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"
]

【讨论】:

【参考方案6】:

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

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

【讨论】:

【参考方案7】:

截至 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 特定的。

以上是关于Javascript:自然排序的字母数字字符串的主要内容,如果未能解决你的问题,请参考以下文章

C中的自然排序 - “字符串数组,包含数字和字母”

使用android对sqlite中的字母数字值进行自然排序

JavaScript - 自然排序 FileList 对象

Microsoft SQL 2005 中的自然(人类字母数字)排序

如何组合两个数据框并自然地对混合字母数字类型的列进行排序?

c#实现自然排序效果,按1,2,11而不是1,11,12,区分字母文字和数字