繁体   English   中英

按字母顺序排列,Array.Sort() 效果不佳

[英]Alphabetical order, Array.Sort() doesn't work well

我的字母顺序有问题。 Array.Sort() 在我的情况下效果不佳。

string str = "AaBbCc";
char[] arr = second.ToCharArray();
Array.Sort(arr);

// Output: ABCabc
// I need: AaBbCc

有任何想法吗?

您可以使用 LINQ 实现您的目标。

首先,我们需要将您的字符分解为一系列单个字符串。 我们可以通过选择做到这一点。

str.Select(x => x.ToString())

好的,现在我们要对这个列表进行排序。 字符串的默认排序是单词排序,它会像aAbBcC一样aAbBcC 由于您想先获取大写字母,我们将使用OrdinalIgnoreCase比较器,它将我们的字符分组。

   .OrderBy(x => x, StringComparer.OrdinalIgnoreCase)

好的,现在我们已经对我们的角色进行了分组。 我们现在要使用 ThenBy 按顺序对这些组进行排序,以使大写字母排在最前面。

   .ThenBy(x => x, StringComparer.Ordinal)

最后,我们可以把它变成一个数组。

   .ToArray();

把它放在一起,我们得到以下内容:

var res = str.Select(x => x.ToString())
             .OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
             .ThenBy(x => x, StringComparer.Ordinal)
             .ToArray();

这给了我们一个看起来像这样的数组: ["A","a","B","b","C","c"]

此时,正如@dvo 所指出的,我们可以通过调用 String.Join 将其转换回单个字符串,如下所示:

var resStr = string.Join("", res);

正如@RufusL 指出的那样,我们可以为这个查询做更多的事情。 因为我们知道我们最终想要一个字符串,所以我们可以完全跳过ToArray调用并坚持使用IOrderedEnumerable<string>

var res = str.Select(x => x.ToString())
             .OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
             .ThenBy(x => x, StringComparer.Ordinal);

因为我们没有用字符串连接任何东西,所以我们可以使用string.Concat而不是string.Join

var resStr = string.Concat(res);

问题是您的代码按 ASCII 值排序,大写 ( AZ ) 为65-90 ,小写 ( az ) 为97-122

假设您总是希望大写字母出现在小写字母之前,我们可以使用一个小技巧,通过在进行比较之前从 ASCII 值中减去31.5 ,将小写字母排在大写值之后。

通过这样做, a变为65.5 ( 97 - 31.5 ) 并将在A ( 65 ) 和B ( 66 ) 之间排序。 对于所有其他小写字母也是如此。 这避免了创建一堆新字符串进行比较和进行多次排序的成本。

例如:

string str = "zZYyabCABcxX";
string ordered = string.Concat(str.OrderBy(c => c > 96 && c < 123 ? c - 31.5 : c));
Console.WriteLine(ordered);

我们还可以使用char.IsUpperchar.ToLower代替硬编码的 ascii 值:

// If the character is lower-case, use the value of its upper-case counterpart plus .5
ordered = string.Concat(str.OrderBy(c => char.IsLower(c) ? char.ToUpper(c) + .5 : c));

输出:

在此处输入图片说明


编辑

要回答其中一条评论,如果您想使用相同的方法对字符进行排序,使小写字母排大写字母之前,那么如果该字符在大写范围内,您将减去32.5 这样a ( 97 ) 变成64.5 ( 97 - 32.5 ) 并且将在A ( 65 ) 之前排序:

string ordered = string.Concat(str.OrderBy(c => c > 96 && c < 123 ? c - 32.5 : c));

或者使用带有char.IsLowerchar.ToUpper的技巧而不是硬编码的 ascii 值:

// If the character is lower-case, use the value of its upper-case counterpart minus .5
ordered = string.Concat(str.OrderBy(c => char.IsLower(c) ? char.ToUpper(c) - .5 : c));

还有一个不需要对 ASCII 值进行数学运算的解决方案:我们可以首先对字符进行排序,以便使用OrderBy(char.ToLower) (按小写字符的 ASCII 值),然后我们可以通过char.IsLower对它们进行char.IsLower ,然后将小写字符排在大写字符之后:

string str = "zZYyabCABcxX";
string ordered = string.Concat(str.OrderBy(char.ToLower).ThenBy(char.IsLower));

// ordered == "AaBbCcXxYyZz"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM