IE8中的Javascript:如何按字母数字属性对对象数组进行排序

6

我有一个JavaScript对象数组,我想通过一个始终为正整数且末尾可以带有一个可选字母的属性进行跨浏览器排序。我正在寻找至少在Firefox 3和Internet Explorer 8中都能工作的解决方案。我找到的最接近这样一个排序函数如下:

var arrayOfObjects = [{id: '1A', name: 'bar', size: 'big'}, {id: '1C', name: 'bar', size: 'small'}, {id: '1', name: 'foo', size: 'big'}, {id: '1F', name: 'bar', size: 'big'}, {id: '1E', name: 'bar', size: 'big'}, {id: '1B', name: 'bar', size: 'small'}, {id: '1D', name: 'bar', size: 'big'}, {id: '1G', name: 'foo', size: 'small'},  {id: '3', name: 'foo', size: 'small'}, {id: '23', name: 'foo', size: 'small'}, {id: '2', name: 'foo', size: 'small'}, {id: '1010', name: 'foo', size: 'small'}, {id: '23C', name: 'foo', size: 'small'}, {id: '15', name: 'foo', size: 'small'}]

arrayOfObjects.sort(function(a, b){
    return (a.id < b.id ? -1 : a.id == b.id ? 0 : 1);
});

经过排序后,打印数组对象的结果如下:

1, foo, big
1010, foo, small
15, foo, small
1A, bar, big
1B, bar, small
1C, bar, small
1D, bar, big
1E, bar, big
1F, bar, big
1G, foo, small
2, foo, small
23, foo, small
23C, foo, small
3, foo, small

然而,我希望数组对象按照以下顺序进行打印:

1, foo, big
1A, bar, big
1B, bar, small
1C, bar, small
1D, bar, big
1E, bar, big
1F, bar, big
1G, foo, small
2, foo, small
3, foo, small
15, foo, small
23, foo, small
23C, foo, small
1010, foo, small

在此基础上,我该如何修改上述函数,以便将对象按数字作为主键、按字母作为次要键进行排序?非常感谢您提供的任何帮助。

3个回答

3
arrayOfObjects.sort((function() {
  var splitter = /^(\d+)([A-Z]*)/;
  return function(a, b) {
    a = a.id.match(splitter); b = b.id.match(splitter);
    var anum = parseInt(a[1], 10), bnum = parseInt(b[1], 10);
    if (anum === bnum)
      return a[2] < b[2] ? -1 : a[2] > b[2] ? 1 : 0;
    return anum - bnum;
  }
})());

这个想法是将键值分为数字部分和字符串部分。

编辑(糟糕,"match"调用反了)

再次编辑 @Ryan Tenney明智地建议匿名外部函数并不是真正必要的:

arrayOfObjects.sort(function(a, b) {
  var splitter = /^(\d+)([A-Z]*)/;
  a = a.id.match(splitter); b = b.id.match(splitter);
  var anum = parseInt(a[1], 10), bnum = parseInt(b[1], 10);
  if (anum === bnum)
    return a[2] < b[2] ? -1 : a[2] > b[2] ? 1 : 0;
  return anum - bnum;     
});

稍微简单一点。


正则表达式字面量不会增加每次迭代的成本。摆脱外部自执行函数并在内部函数中声明splitter是更好的选择。 - Ryan Tenney
除此之外,非常好的回答。比我准备提供的答案要简洁得多 :) - Ryan Tenney
我对于重复使用正则表达式有些偏执——这并不是因为性能问题,而是为了方便维护。或许在一百次尝试中,只有一次我能够一次性地写出正确的正则表达式,所以我想要尽量减少重复使用的次数。当然,我也可以将其作为变量直接输入函数体中,只是当时没有考虑那么多。 - Pointy
我认为你可以将第一个return改为return a[2] < b[2] ? -1 : 1;,并且它仍然可以正确排序。 - Cristian Sanchez
1
@Daniel 嗯,那可能会使排序变得不稳定 - “稳定”排序是指将已经按键相等顺序排好的元素保持在同一顺序中,而“不稳定”排序可以重新排列它们。虽然我不能确定这是真的,但我总是明确检查键相等性,以便稳定排序确实保持稳定。 - Pointy
@Pointy:我记得Chrome的sort实现仍然是不稳定的,因此即使两个元素相等,它们的位置在最终状态中仍可能被交换。其他浏览器的排序实现是稳定的。我想还是安全第一比较好。 - Cristian Sanchez

0
你不需要从数字字符串中解析整数-
如果两个数字字符串匹配,值无关紧要,可以查看可能的字母。
如果数字不匹配,则将一个数字减去另一个数字强制转换。
var rx=/^(\d+)(\D?)$/;

    arrayOfObjects.sort(function(a, b){ 
        var id_a= a.id.match(rx), id_b= b.id.match(rx);
        if(id_a[1]== id_b[1]){
            if(id_a[2]=== id_b[2]) return 0;
            else{
                if(!id_a[2]) return -1;
                if(!id_b[2]) return 1;
                return id_a[2]> id_b[2]? 1: -1;
            }
        }
        return id_a[1]-id_b[1];
    });

0

这是一个比较函数,使用更详细的代码和有意义的变量名:

/**
* Sort array ba numerical & alphabetical order ["1a", "2z", "2a", 99, 100]
*/
function compare(a, b) { 

    var re = /(\d+)([^ ]?)/, numA, numB, charA, charB,
        aMatches = re.exec(a),
        bMatches = re.exec(b) ;

    numA = aMatches[1] ? aMatches[1] : ''; //get the number part
    charA = aMatches[2] ? aMatches[2] : ''; //get the char part

    numB = bMatches[1] ? bMatches[1] : '';
    charB = bMatches[2] ? bMatches[2] : '';

    if (charA || charB){ //if one or both of the compare candidates have letter
        if (numA==numB){ //only if number parts are equal
            return charA.localeCompare(charB); // we compare letters 
        }
    }

    return numA - numB; // otherwise just compare numbers
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接