【问题标题】:Understanding sort() compareFunction理解 sort() compareFunction
【发布时间】:2013-07-07 21:05:18
【问题描述】:

我正在使用的电子商务平台无法对我们的产品属性字段的选项进行重新排序。这真的很糟糕,因为要插入一个新选项,您几乎必须删除所有现有选项并重新开始。我正在尝试在客户端进行。这是我正在使用的东西(这个是鞋码):

  • 9 EE
  • 9 1/2 EE
  • 10 EE
  • 10 1/2 EE
  • 11 EE
  • 11 1/2 EE
  • 9 EEEE
  • 9 1/2 天
  • 9 1/2 EEEE
  • 10 EEEE
  • 10 1/2 EEEE
  • 11 EEEE
  • 9 天
  • 11 1/2 EEEE

这些实际上是一些<option>s 的文本形式。值的格式为X Y Z,其中:

  • X 是一个整数
  • Y 是字符串“1/2”,可能不存在
  • Z 是一个字母代码,可以是“D”、“E”、“EEE”或“EEEE”,可能不存在

上面的期望顺序是这样的:

  • 9 天
  • 9 1/2 天
  • 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() 函数有所了解,但无法完全理解您可以传递给它的比较函数是如何工作的。到目前为止我已经得到了这个:

<select>
    <option>9 EE</option>
    <option>9 1/2 EE</option>
    <option>10 EE</option>
    <option>10 1/2 EE</option>
    <option>11 EE</option>
    <option>11 1/2 EE</option>
    <option>9 EEEE</option>
    <option>9 1/2 D</option>
    <option>9 1/2 EEEE</option>
    <option>10 EEEE</option>
    <option>10 1/2 EEEE</option>
    <option>11 EEEE</option>
    <option>9 D</option>
    <option>11 1/2 EEEE</option>
</select>

我从这个答案中的代码开始: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 天
  • 9 1/2 EE
  • 9 1/2 EEEE
  • 9 天
  • 9 EE
  • 9 EEEE

我在 javascript 中看到 '11' &gt; '9' 返回 false,这对我来说毫无意义。

MDN describes the compare function argument 这样,我有点明白:

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

...但我不知道如何调整它以满足我的要求。我已经尝试了一些东西,但我只是觉得我在黑暗中拍摄。我试图表明我花了一些时间来尝试理解这个问题。我有兴趣了解更多信息,但现在我只想解决这个问题。

http://jsfiddle.net/DnwJ6/有线索吗?

【问题讨论】:

  • 我认为这里的解决方案是以某种方式将ab 标准化为\d\d (1/2|0/0) [a-z]* 这样的格式,然后再进行比较
  • "11" &gt; "9" === false 非常有意义。您正在比较 字符串。尝试先转换为数字
  • @Ian 我知道它们是字符串,但 11 是比 9 更长的字符串,数值无关紧要。是因为它只看第一个字符吗?发生了什么,正在比较什么?我不知道如何将这些值转换为数字。
  • @WesleyMurch:比较字符串和字符串长度是两件不同的事情。以“蝙蝠”和“杏”这两个词为例。如果比较字符串长度,那么字典中的“bat”应该排在“apricot”之前。它不这样做吗?你如何比较它?想想吧。

标签: javascript jquery


【解决方案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(/[a-z]/i.test(parts[1])){
            op += '0/0' + parts[1];
        } else {
            op += parts[1]
        }
    }

    op += parts.length > 2 ? parts[2] : '';
    return op;
}

演示:Fiddle

如果有人可以提出任何解决方案来进一步优化它,那就太好了

【讨论】:

  • 它与所需的输出不太匹配(查看 1/2 大小),但现在肯定可以。稍后我将不得不更多地考虑这一点,这样我才能理解它,现在我只需要一些代码来猛击网站,这样我就可以上床睡觉了。谢谢。
  • @WesleyMurch 更好一点的可以在jsfiddle.net/arunpjohny/ZnLJh/4找到
  • 我正在尝试匹配帖子中所需的输出。所有这些解决方案都比我现有的更好,所以我现在就使用它,再次感谢。
【解决方案2】:

按照您想要的顺序将值放入数组中:

var shoeSizes = [ '9 D','9 1/2 D','9 EE','9 1/2 EE', ...]

使用Array.protoype.indexOf (with a shim for older browsers) 获取数组中匹配文本的索引。使用值的索引进行比较,例如:

function(a,b) {
  return shoeSizes.indexOf(a) - shoeSizes.indexOf(b);
}

如果您需要处理不在数组中的值,请测试 indexOf 返回的值,如果它是 -1,则替换为默认值。

或者,您可以将大小设置为对象中属性值的名称并分配特定值:

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

【讨论】:

  • 我正在使用现有的 HTML,所以在我可以应用它之前,我必须仔细研究一下,谢谢你的回答。
  • 我绝对不能使用这些选项中的任何一个。客户提供值,我只想盲目地看字符串。我不确定 "将值按您想要的顺序放入数组中" 在这里如何有用,如果我能做到的话,我的问题就已经解决了。我不知道值是什么,只是格式。不过还是谢谢。
  • 如果你不知道值是什么,你怎么知道如何排序呢?您似乎想对一些东西进行排序,但事先不知道如何排序。
【解决方案3】:

看看这个:

$("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]); }
}));

它使用一个正则表达式分解各个部分,然后根据每个组件进行比较。

Demo fiddle.

【讨论】:

  • 太棒了。希望对您有所帮助。顺便说一句,'11' &gt; '9' 返回false,因为它是一个字符串比较。你看,在比较它们以使用它们的数字值之前,我在字符串上使用了parseInt()
  • 当字符串与模式完全不匹配时会出现几个问题(未定义的索引错误),因此我必须先进行一些防御,然后才能全局使用它。明天要测试,我已经筋疲力尽了 - 再次感谢。
  • 让我知道这些模式是什么。我们可以使正则表达式更加灵活。明天过来,我们会根据需要进行调整。
  • 我正在使用“Miva Merchant”,这对于向您出售插件以实现基本功能的第三方开发人员来说是臭名昭著的,所以没过多久我就决定不存在排序功能。几天后我终于找到了它facepalm.gif,所以我不再需要它了,但我真的很感激所有的答案。绝对有帮助。
【解决方案4】:

我看到您已经有了一个可行的解决方案,但只是为了比较(双关语),这是许多其他方法之一 (fiddle):

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

此方法创建每个鞋码的文本表示,可直接作为文本排序。然后它使用这些文本表示进行排序。

【讨论】:

  • 谢谢,我想我现在一般都知道该怎么做了。事实证明,我必须在这方面做更多的工作/测试才能让它在商店里的每一种产品上都能在全球范围内工作,所以我最终可能会改变接受的答案,我只是不想让人们也花钱虽然我有一个“足够好”的答案,但我花了很多时间在我相当狭窄的问题上,但答案肯定有助于我下次理解。
【解决方案5】:

首先要了解的是,如果您对文本项而不是数字进行排序,12确实小于9。这是因为&lt;1&gt; &lt;2&gt; 小于&lt;9&gt; &lt;nothing&gt; 因为19 在这种情况下是“主键”。

您面临的第二个问题是长度(9 1/2)是主键还是宽度(EE)是主键。我怀疑前者会更有意义,所以在此基础上继续。

决定之后,最好的办法是为调用提供一个排序函数,该函数将每个字符串转换为一个数值,然后比较该值。例如:

  1. 获取第一个(空格分隔)字段 (9) 并将您的值设置为该字段。
  2. 如果下一个字段存在并且是1/2,则将0.5 添加到该值。
  3. 如果最后一个字段存在(alpha 字段),只需将其转换为小于0.5 的值并添加它(例如,a -> 0.01、B -> 0.02、...、@ 987654334@ -> 0.08 等等)。

最后一个取决于宽度的相对顺序,我选择了典型的美国系统。

您最终会得到一个指示正确排序的值,然后您的排序函数可以简单地进行数字比较。一个例子如下:

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 个元素的数组,并获取第一个元素的数值。如果第二个和第三个不存在,则返回此值(处理“裸”大小,如 913)。

否则它决定它是否是一个半增量长度 - 这取决于第二个字段是否为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

【讨论】:

    【解决方案6】:

    您首先需要定义一个适当的排序键,您可以使用它进行合理的比较;以下函数使用正则表达式来挖掘有用的信息:

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

    Demo

    【讨论】:

    • 大家点赞,今晚我无法真正让所有这些信息深入了解,明天必须通读。就目前而言,接受用勺子喂给我的答案正是我所需要的。
    • @WesleyMurch 够公平的;这种特殊方法的优点是构建排序键的逻辑与实际排序过程解耦,使得这两个部分更容易适应:)
    • @WesleyMurch 再次查看代码,我意识到我犯了一个错误;数组的比较强制进行基于字符串的比较 :( 更新了代码以仍然使用排序键生成器函数。
    猜你喜欢
    • 2013-10-29
    • 2020-10-19
    • 1970-01-01
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    • 2013-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多