sorting数组元素(带数字的string),自然sorting

我有一个数组像;

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

并且需要对它进行sorting

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

我已经尝试了一个sortingfunction;

 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"] 

我试图想到一个正则expression式可以工作,但无法绕开它。
如果有帮助,格式将始终是2个字母,x个数字,然后是任意数量的字符。

这被称为“自然sorting”,可以像这样在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)); 
 var re = /([az]+)(\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; }); 

我很喜欢georg的解决scheme,但是我需要用下划线(“_”)来sorting数字。 以下是我如何修改他的代码:

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

用前导零填充string中的数字,然后正常sorting。

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

你可以像这样做一个正则expression式来获得string的非数字和数字部分:

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

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

然后在你的比较函数中,你可以遍历两个string中的一部分。 第一个不匹配的部分决定整体比较的结果。 对于每个部分,检查部分是否以数字开始,如果是,则在进行比较之前将其parsing为数字。

添加一个替代(为什么不):

 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(/^([az]+)([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的例子

不漂亮,但检查前两个字符代码。 如果全部平等parsing并比较数字:

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

你可以使用String#localeCompareoptions

灵敏度

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

  • "base" :只有基数不同的string比较不等。 例子: a ≠ ba = áa = A
  • "accent" :只有在基本字母或重音和其他变音标记上有所不同的string才会被比较为不相等。 例子: a ≠ ba ≠ áa = A
  • "case" :只有基数不同的string或大小写比较不等的string。 例子: a ≠ ba = áa ≠ A
  • "variant" :基本字母,重音和其他变音标记不同的string,或情况比较不等的string。 其他差异也可能被考虑在内。 例子: a ≠ ba ≠ áa ≠ A

“sort”的使用默认是“variant”; 它的语言环境依赖于使用“search”。

数字

是否应使用数字sorting,例如“1”<“2”<“10”。 可能的值是truefalse ; 默认是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);