按字典顺序排序任何东西

问题描述

我有这个代码,我想我几年前在互联网上找到了它,但它不太好用。

目的是获取任何字符串并从中创建一个按字典顺序按大数排序的字符串 - 因为然后可以通过从另一个更大的数字中减去该数字来实现逆(降序)排序。

private static BigInteger maxSort = new BigInteger(Encoding.Unicode.GetBytes("5335522543087813528200259404529154678271640415603227881439560533607051111046319775598721171814499900"));

public static string GetSortString(string str,bool descending)
{
    var sortNumber = new BigInteger(Encoding.Unicode.GetBytes(str));

    if (descending)
    {
        sortNumber = maxSort - sortNumber;
    }

    return "$SORT!" + sortNumber.ToString().PadLeft(100,'0') + ":" + str;
}

我需要它的原因是我想用它作为 RowKey 插入 Azure 表存储中,这是在表存储中排序的唯一方法。我需要对任何文本、任何数字和任何日期进行升序和降序排序。

任何人都可以看到代码的问题或有任何用于相同目的的代码吗?

该问题用 C# 标记,但这当然不是语法问题,因此如果您在任何其他代码中也有答案也可以。

示例

我想将任何字符串转换为按字典顺序正确排序的数字 - 因为如果它是一个数字,那么我可以将它取反并降序排序。

例如,如果我可以转换:

ABBA    to 1234
Beatles to 3131
ZZ Top  to 9584

然后这些数字会正确地对它们进行排序......而且,如果我从一个大数字中减去它们,我将能够反转排序顺序:

10000 - 1234 = 8766
10000 - 3131 = 6869
10000 - 9584 = 0416

当然,为了支持更长的文本输入,我需要从一个非常大的数字中减去它们,这就是我使用非常大的 BigInteger 的原因。

代码的当前输出

ABBA:    $SORT!0000000000000000000000018296156958359617:ABBA
Beatles: $SORT!0000000009111360792640460912278748069954:Beatles
ZZ TOP:  $SORT!0000000000000096715522885596192519618650:ZZ TOP

如您所见,最长的文本获得最高的数字。我还尝试在输入 str 上立即添加填充,但这也无济于事。

回答

接受的答案有效。对于降序排序,可以使用上面的“BigInteger”技巧。

可排序字符串的长度有一些限制。

这是最终代码

private static BigInteger maxSort = new BigInteger(Encoding.Unicode.GetBytes("5335522543087813528200259404529154678271640415603227881439560533607051111046319775598721171814499900"));

public static string GetSortString(string str,bool descending)
{
    BigInteger result = 0;
    int maxLength = 42;

    foreach (var c in str.tochararray())
    {
        result = result * 256 + c;
    }

    for (int i = str.Length; i < maxLength; i++)
    {
        result = result * 256;
    }

    if (descending)
    {
        result = maxSort - result;
    }

    return "$SORT!" + result;
}

解决方法

如果您正在寻找一种方法来为任何字符串赋予一个值,以便您可以根据数字对它们进行相应的排序并获得与上述相同的结果,那么您不能。原因是字符串没有任何长度限制。因为您总是可以向字符串添加一个字符,从而获得更大的数字,即使它应该具有较低的字典值。

如果他们有长度限制,你可以这样做

伪代码

bignum res = 0;
maxLength = 42;
for (char c : string)
  res = res * 256 + c
for (int i = string.length; i < maxLength; i++)
  res = res *256

如果你想优化一下,最后一个循环可以是一个查找表。如果你只使用 a-z,那么 256 次可以减少到 26 或 32 次。