JavaScript 自定义排序,带有特殊字符破折号 (-) 和下划线 (_)。

3

我正在尝试执行自定义排序,如以下顺序:

  1. 特殊字符(-首先,_最后)
  2. 数字
  3. 字母

例如,如果我对以下内容进行排序

var words = ['MBC-PEP-1', 'MBC-PEP01', 'MBC-PEP91', 'MBC-PEPA1', 'MBC-PEPZ1', 'MBC-PEP_1'];

result should be

MBC-PEP-1,MBC-PEP_1,MBC-PEP01,MBC-PEP91,MBC-PEPA1,MBC-PEPZ1

使用我的代码得到的结果如下:

"MBC-PEP-1", "MBC-PEP01", "MBC-PEP91", "MBC-PEP_1", "MBC-PEPA1", "MBC-PEPZ1"

但我需要上述的排序顺序,不确定如何实现。

function MySort(alphabet)
{
    return function(a, b) {
        var lowerA = a.toLowerCase()
        var lowerB = b.toLowerCase()
        var index_a = alphabet.indexOf(lowerA[0]),
        index_b = alphabet.indexOf(lowerB[0]);

        if (index_a === index_b) {
            // same first character, sort regular
            if (a < b) {
                return -1;
            } else if (a > b) {
                return 1;
            }
            return 0;
        } else {
            return index_a - index_b;
        }
    }
}

var items = ['MBC-PEP-1', 'MBC-PEP01', 'MBC-PEP91', 'MBC-PEPA1', 'MBC-PEPZ1', 'MBC-PEP_1'],
sorter = MySort('-_0123456789abcdefghijklmnopqrstuvwxyz');

console.log(items.sort(sorter));

3个回答

1

我将这里的答案移植到了JavaScript中,它可以在不使用递归或过于复杂的任何东西的情况下完成你想要的操作:

function MySort(alphabet) {
    return function (a, b) {
       a = a.toLowerCase();
       b = b.toLowerCase();
       var pos1 = 0;
       var pos2 = 0;
       for (var i = 0; i < Math.min(a.length, b.length) && pos1 == pos2; i++) {
          pos1 = alphabet.indexOf(a[i]);
          pos2 = alphabet.indexOf(b[i]);
       }

       if (pos1 == pos2 && a.length != b.length) {
           return o1.length - o2.length;
       }

       return pos1 - pos2;
    };
}
    
var items = ['MBC-PEP-1', 'MBC-PEP01', 'MBC-PEP91', 'MBC-PEPA1', 'MBC-PEPZ1', 'MBC-PEP_1'],
sorter = MySort('-_0123456789abcdefghijklmnopqrstuvwxyz');

console.log(items.sort(sorter));


1
如Narigo在他们的回答中所说,您只比较了第一个字符。这里有一个不同的想法,可能更简单:

function MySort(a, b) {
  a = a.replace("_", ".");
  b = b.replace("_", ".");
  return a.localeCompare(b);
}

var items = ['MBC-PEP-1', 'MBC-PEP01', 'MBC-PEP91', 'MBC-PEPA1', 'MBC-PEPZ1', 'MBC-PEP_1'];

console.log(items.sort(MySort));

我们基本上使用普通的字符串比较,只是将下划线改为点来决定排序,因为它与你要实现的目标兼容。

1
我不建议使用这个答案,因为如果输入中突然包含了 . 并且需要按照不同的顺序指定,那么会发生什么呢?维护起来很困难,因为你无法真正定义所需的顺序。 - Cᴏʀʏ
如果字符串格式预期更通用,则应在问题中说明;)我认为这是给定信息的最佳答案。 - Patrick Roberts
我必须在这里同意Cory的观点。对我来说,能够使用本地函数是一种创造性的黑客方式,但它并不像一个可维护的解决方案。 - Narigo
绝对有一些好的观点。虽然我认为在字符串中有一个点并不是问题,它只会像正常一样排序。其他发布的解决方案也是这样做或完全忽略了这种情况。 - Aioros
1
@Aioros 我不同意他们关于这是“hack”的看法,但我确实认可这个基准测试,它证明了你的解决方案是最慢的。这实际上是由于localeCompare()的内部复杂性而不是你的方法。我建议使用return -(a < b) || +(a > b);,我在这个答案中解释了原因,正如基准测试所示,它超越了所有这些答案。 - Patrick Roberts
哦,事实证明,在对大型数组进行排序之前和之后替换字符串可以使其更快。 - Patrick Roberts

-1

你的算法只考虑了字符串的第一个字符,需要同时检查更多的字符串/后续字符。以下是使用递归的快速解决方案:

function MySort(alphabet)
{
    return function recSorter(a, b) {
        var lowerA = a.toLowerCase()
        var lowerB = b.toLowerCase()
        var index_a = alphabet.indexOf(lowerA[0]),
        index_b = alphabet.indexOf(lowerB[0]);

        if (index_a === index_b && index_a >= 0) {
            return recSorter(a.slice(1), b.slice(1));
        } else {
            return index_a - index_b;
        }
    }
}

var items = ['MBC-PEP-1', 'MBC-PEP01', 'MBC-PEP91', 'MBC-PEPA1', 'MBC-PEPZ1', 'MBC-PEP_1'],
sorter = MySort('-_0123456789abcdefghijklmnopqrstuvwxyz');

console.log(items.sort(sorter));

我不确定当您有不同长度的字符串、字母表外的字符或其他边缘情况时,您希望发生什么。对于发布的示例,这将导致预期的顺序。


2
一个递归的、有作用域的排序方法?对于一个简单的字符串比较算法来说,这似乎太昂贵了... - Patrick Roberts
我不建议将此用于性能奖项。如果您想按照自己的字母表排序,例如按键盘上的出现顺序(qwertyuiopasdfghjklzxcvbnm 而不是 a-z),仍然需要映射到自己的索引并进行比较。您可能会更快地编写命令式循环,但由于这是尾递归、可读性好,并且可能比替换源字符串中的字符更少的 hack,所以我会保留这个答案... ;) - Narigo
如果你能提到一个实现了JavaScript尾调用优化的例子,我会很惊讶,所以我不确定指出它是尾递归的意义何在。我也不会把这个代码称为可读性强或者“不那么hack”的代码。ASCII排序是相当常见的,因此只要在简短的注释中记录意图,利用简单的字符串替换就足够了。 - Patrick Roberts
对我来说,尾递归通常更容易理解。问题是,替换字符串然后比较这些替换后的字符串对我来说不是一个好的策略。如果你想获得最佳性能,使用Cory的解决方案。 - Narigo
似乎这取决于您使用的浏览器及其版本。在此,您可以尝试一下:https://jsperf.com/sort-by-alphabet/1 有很多东西可以进行微调,但我想每个人都必须自己决定需要什么解决方案。显然已经有足够的选择了。 - Narigo
显示剩余2条评论

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