2010-05-10 177 views
69

我正在寻找最简单的方法来排序由数字和文本组成的数组,以及它们的组合。Javascript:自然排序的字母数字字符串

E.g.

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

变成

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

这将在组合使用与所述溶液another question I've asked here

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

我正在用JavaScript写这篇文章。

编辑:adormitu指出,我正在寻找的是自然排序

+0

也看到'请问你http://stackoverflow.com/questions/51165做字符串比较在JavaScript'? /你怎么做字符串比较在JavaScript中 – 2014-09-26 12:19:58

+1

原来的问题在2010年被问到,所以它不会令人惊讶:) – ptrn 2015-07-23 21:32:02

回答

102

这现在可以在现代浏览器中使用localeCompare。通过传递numeric: true选项,它会巧妙地识别数字。您可以使用sensitivity: 'base'进行不区分大小写。经过Chrome,Firefox和IE11测试。

下面是一个例子。它返回1,这意味着2后10云:

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

出于性能方面的分拣大量的字符串时,文章说:

在比较大的数字的字符串,如在分拣大数组,最好创建一个Intl.Collat​​or对象并使用它的compare属性提供的函数。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare

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

upvote为现代单线+1! – A1rPun 2016-11-07 16:45:00

+1

真棒,很好的方法 – 2017-05-23 06:30:37

+1

真棒,你解决了我的问题。谢谢 – 2017-09-11 06:05:07

38

功能所以你需要一个自然排序

如果是这样,那么也许this script by Brian Huisman based on David koelle's work将是你所需要的。

好像布赖恩·豪氏威马的解决方案现在直接托管在大卫Koelle的博客:

+0

正确的,自然的排序是我正在寻找。我会寻找你发送的链接,谢谢 – ptrn 2010-05-10 12:32:22

+0

这当然有诀窍。非常感谢! – ptrn 2010-05-10 16:06:39

+0

这是非常不自然的一种。它不会产生一种alphbetic排序。 – tchrist 2011-08-16 01:28:09

17

对值进行比较,你可以使用一个比较方法 -

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

但为了快速排序ng数组,在排序之前对数组进行索引,因此您只需执行一次小写转换和正则表达式 而不是每一步中的排序。

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]; 
    } 
} 
+0

这个工作在我的情况下,内部数组决定外部的顺序? – ptrn 2010-05-10 13:11:58

+0

什么是'String.prototype.tlc()'?这是你自己的代码还是你从某个地方得到它?如果是后者,请链接到页面。 – 2010-05-10 13:15:22

+0

对错误更正抱歉,谢谢。 如果您希望[1]和b [1]控制排序,请使用 a = String(a [1])。toLowerCase(); b =字符串(b [1])。toLowerCase(); – kennebec 2010-05-10 13:17:07

0

大厦@Adrien要我们回答以上,并使用该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"]; 
    } 
} 
0

成像一个8位数的填充函数,分别将'123asd'和'19asd'转换为'00000123asd'和'00000019asd'。使用这些版本的字符串将帮助我们对“19asd”进行排序,使其出现在“123asd”之前。

我们可以使用正则表达式/\d+/g,以帮助找到所有需要补齐的数字:

str.replace(/\d+/g, pad) 

下面演示了使用这种技术的排序:

var list = [ 
 
    '123asd', 
 
    '19asd', 
 
    '12345asd', 
 
    'asd123', 
 
    'asd12' 
 
]; 
 

 
function pad(n) { return ("00000000" + n).substr(-8); } 
 
function natural_expand(a) { return a.replace(/\d+/g, pad) }; 
 
function natural_compare(a, b) { 
 
    return natural_expand(a).localeCompare(natural_expand(b)); 
 
} 
 

 
console.log(list.map(natural_expand).sort()); // intermediate values 
 
console.log(list.sort(natural_compare)); // result

中间结果显示了natural_expand()例程的功能并为您提供了随后natural_compare程序将如何工作的理解:

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

输出:

[ 
    "19asd", 
    "123asd", 
    "12345asd", 
    "asd12", 
    "asd123" 
] 
相关问题