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#localeCompare
和options
灵敏度
string中的哪些差异应导致非零结果值。 可能的值是:
"base"
:只有基数不同的string比较不等。 例子:a ≠ b
,a = á
,a = A
"accent"
:只有在基本字母或重音和其他变音标记上有所不同的string才会被比较为不相等。 例子:a ≠ b
,a ≠ á
,a = A
"case"
:只有基数不同的string或大小写比较不等的string。 例子:a ≠ b
,a = á
,a ≠ A
。"variant"
:基本字母,重音和其他变音标记不同的string,或情况比较不等的string。 其他差异也可能被考虑在内。 例子:a ≠ b
,a ≠ á
,a ≠ A
。“sort”的使用默认是“variant”; 它的语言环境依赖于使用“search”。
数字
是否应使用数字sorting,例如“1”<“2”<“10”。 可能的值是
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);