理解sort()compareFunction
我正在使用一个电子商务平台,该平台缺乏重新排序产品属性字段选项的能力。 它真的很糟糕,因为要插入一个新选项,你几乎必须删除所有现有选项并重新开始。 我试图改为客户端。 这是我正在使用的(这是鞋子尺码):
- 9 EE
- 9 1/2 EE
- 10 EE
- 10 1/2 EE
- 11 EE
- 11 1/2 EE
- 9 EEEE
- 9 1/2 D
- 9 1/2 EEEE
- 10 EEEE
- 10 1/2 EEEE
- 11 EEEE
- 9 D.
- 11 1/2 EEEE
这些实际上是表单中某些的文本。 值的格式为
XYZ
,其中:
-
X
是整数 -
Y
是字符串“1/2”,可能不存在 -
Z
是字母代码,可以是“D”,“E”,“EEE”或“EEEE”,也可能不存在
以上所需的顺序是这样的:
- 9 D.
- 9 1/2 D
- 9 EE
- 9 1/2 EE
- 9 EEEE
- 9 1/2 EEEE
- 10 EE
- 10 1/2 EE
- 10 EEEE
- 10 1/2 EEEE
- 11 EE
- 11 1/2 EE
- 11 EEEE
- 11 1/2 EEEE
我已经学习了一些关于javascript的sort()
函数的知识但是还没能完全理解你可以传递给它的比较函数是如何工作的。 到目前为止我有这个:
9 EE 9 1/2 EE 10 EE 10 1/2 EE 11 EE 11 1/2 EE 9 EEEE 9 1/2 D 9 1/2 EEEE 10 EEEE 10 1/2 EEEE 11 EEEE 9 D 11 1/2 EEEE
我从这个答案的代码开始: https : //stackoverflow.com/a/667198/398242
$("select").html($("option").sort(function (a, b) { return a.text == b.text ? 0 : a.text < b.text ? -1 : 1 }));
对这样的项目进行排序(即使是第一个标准也不起作用):
- 10 1/2 EE
- 10 1/2 EEEE
- 10 EE
- 10 EEEE
- 11 1/2 EE
- 11 1/2 EEEE
- 11 EE
- 11 EEEE
- 9 1/2 D
- 9 1/2 EE
- 9 1/2 EEEE
- 9 D.
- 9 EE
- 9 EEEE
我看到在javascript '11' > '9'
返回false
,这对我来说没有任何意义。
MDN描述了比较函数参数 ,我得到了它:
function compare(a, b) { if (a is less than b by some ordering criterion) return -1; if (a is greater than b by the ordering criterion) return 1; // a must be equal to b return 0; }
…但我还没有弄清楚如何调整它以满足我的要求。 我尝试了一些东西,但我觉得我在黑暗中拍摄。 我试图certificate我花了一些时间来尝试理解这个问题。 我有兴趣学习更多,但是现在我只想解决这个问题。
http://jsfiddle.net/DnwJ6/任何线索?
检查一下:
$("select").html($("option").sort(function (a, b) { var regex = /(\d+)((?: 1\/2)? )([DE]+)/; var abreakdown = a.text.match(regex), bbreakdown = b.text.match(regex); if (parseInt(abreakdown[1]) === parseInt(bbreakdown[1])) { if (abreakdown[3] === bbreakdown[3]) { return (abreakdown[2] === bbreakdown[2] ? 0 : (abreakdown[2] < bbreakdown[2] ? -1 : 1)); } else { return abreakdown[3] < bbreakdown[3] ? -1 : 1; } } else { return parseInt(abreakdown[1]) - parseInt(bbreakdown[1]); } }));
它使用正则表达式来分解碎片,然后根据每个组件进行比较。
演示小提琴。
由于您已经格式化了文本,因此一种方法是在比较文本元素之前对其进行规范化。
这个解决方案可能不是那么理想,但可以完成这项工作
$("select").html($("option").sort(function (a, b) { return nomalize(a.text) < nomalize(b.text) ? -1 : 1; })); function nomalize(val){ var parts = val.split(' '), op = ''; op = parts[0].length == 1 ? '0' + parts[0] : parts[0]; if(parts.length > 1){ if(/[az]/i.test(parts[1])){ op += '0/0' + parts[1]; } else { op += parts[1] } } op += parts.length > 2 ? parts[2] : ''; return op; }
演示: 小提琴
如果有人可以建议任何解决方案来进一步优化它将会很棒
按照您希望的顺序将值放在数组中:
var shoeSizes = [ '9 D','9 1/2 D','9 EE','9 1/2 EE', ...]
使用Array.protoype.indexOf ( 旧版浏览器使用shim )获取数组中匹配文本的索引。 使用索引进行比较,例如:
function(a,b) { return shoeSizes.indexOf(a) - shoeSizes.indexOf(b); }
如果需要处理数组中不存在的值,请测试indexOf返回的值,如果为-1则替换为default。
或者,您可以将大小设置为对象中属性值的名称并指定特定值:
var shoeSizes = { '9 D': 5, '9 1/2 D': 10, '9 EE': 15, '9 1/2 EE': 20, ...};
然后使用比较中的值:
function(a,b) { return shoeSizes[a] - shoeSizes[b]; }
或者允许默认值:
function(a,b) { return (a in shoeSizes? shoeSizes[a] : 1000) - (b in shoeSizes? shoeSizes[b] : 1000); }
我看到你已经有了一个有效的解决方案,但只是为了比较(双关语),这是其他许多方法之一( 小提琴 ):
// Make a shoe size sortable as text function sortableSize( text ) { // Split the size into parts separated by spaces var parts = text.split( ' ' ); // The first part is the size number; // make sure it is two digits (09,10,etc.) if( parts[0].length == 1 ) parts[0] = '0' + parts[0]; // If it wasn't a 1/2 size, make it 0/2 if( parts.length == 2 ) parts.splice( 1, 0, '0/2' ); // So '9 EE' becomes '09 0/2 EE' return parts.join( ' ' ); } var $options = $('#sizes option'); $options = $options.sort( function( a, b ) { a = sortableSize( a.text ); b = sortableSize( b.text ); return a < b ? -1 : a > b ? 1 : 0; }); $('#sizes').html( $options );
此方法创建每个鞋号的文本表示,可直接排序为文本。 然后它使用那些文本表示进行排序。
你必须要理解的第一件事是,如果你要对文本项而不是数字进行排序,那么12
确实小于9
。 这是因为<1>
<2>
小于<9>
因为在这种情况下1
和9
是“主键”。
您面临的第二个问题是长度(9 1/2)是主键还是宽度(EE)。 我怀疑前者会更有意义,所以继续在此基础上。
确定之后,最好的办法是为调用提供一个排序函数,将每个字符串转换为数值,然后比较该值。 例如:
- 获取第一个(空格分隔)字段(9)并将值设置为该值。
- 如果下一个字段存在且为
1/2
,则将该值加0.5
。 - 如果存在最后一个字段(alpha字段),只需将其转换为小于
0.5
某个值并添加它(例如,a
– > 0.01,B
– > 0.02,…,EEEE
– > 0.08,依此类推)。
最后一个取决于宽度的相对排序,我选择了一个典型的美国系统。
你最终得到的是一个值,它决定了正确的排序,你的排序function可以简单地进行数值比较。 一个例子如下:
function xlat(s) { var s2 = s.split(" "); var n = parseInt(s2[0]); if (s2.length == 1) { return n; } var last = s2[1]; if (last == '1/2') { n = n + 0.5; if (s2.length == 2) { return n; } last = s2[2]; } var widths = ['A','B','C','D','E','EE','EEE','EEEE','F','G']; n = n + widths.indexOf(last) / 100; return n; } $("select").html($("option").sort(function (a, b) { var na = xlat(a.text); var nb = xlat(b.text); return na == nb ? 0 : na < nb ? -1 : 1; }));
xlat
函数在这里很重要。 它首先将大小拆分为1,2或3个元素的数组,并获取第一个的数值。 如果第二个和第三个不存在,则返回此值(处理“裸”大小,如9
或13
)。
否则它决定它是否是半增量长度 - 如果第二个字段是1/2
则决定。 此时,它还会检测是否没有宽度并返回大小。
一旦超过这一点,我们就有了大小(整数或一半), last
变量保持宽度。 然后,我们只需在数组中添加一个基于此大小位置的值,进行适当修改(除以100),这样它就不会影响主键。
通过使用您自己的代码,您可以得到(正如预期的那样):
9 D 9 EE 9 EEEE 9 1/2 D 9 1/2 EE 9 1/2 EEEE 10 EE 10 EEEE 10 1/2 EE 10 1/2 EEEE 11 EE 11 EEEE 11 1/2 EE 11 1/2 EEEE
首先需要定义一个合适的排序键,您可以使用它进行合理的比较; 以下函数使用正则表达式来挖掘有用的信息位:
function sortkey(val) { var matches = val.match(/^(\d+)( 1\/2)? (\w+)$/), number = +matches[1]; if (matches[2]) { number += 0.5; // add "1/2" } return [number, matches[3]]; }
第一场比赛被投入一个数字; 如果第二个匹配可用,则添加0.5
。 然后,将最后一个匹配作为辅助排序键添加。 返回值是这样的:
[9.5, 'EE']
然后,此结构可用于比较function:
function compareFunc(a, b) { var sa = sortkey(a.text), sb = sortkey(b.text); if (sa[0] == sb[0]) { return sa[1] < sb[1] ? -1 : 1; } else { return sa[0] < sb[0] ? -1 : 1; } }
适用于您的特定代码:
var $sorted = $('select > option').sort(compareFunc); $('select').html($sorted);
演示