简体   繁体   English

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

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

I have problem with alphabetical order.我的字母顺序有问题。 Array.Sort() doesn't work well in my case. Array.Sort() 在我的情况下效果不佳。

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

// Output: ABCabc
// I need: AaBbCc

Any ideas?有任何想法吗?

You can achieve what you're looking for with LINQ.您可以使用 LINQ 实现您的目标。

First, we need to break your characters up into a sequence of single character strings.首先,我们需要将您的字符分解为一系列单个字符串。 We can do that with select.我们可以通过选择做到这一点。

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

Okay, now we want to order this list.好的,现在我们要对这个列表进行排序。 The default sort for strings is word sort, which would order it like aAbBcC .字符串的默认排序是单词排序,它会像aAbBcC一样aAbBcC Since you want to get the capitals first, we'll use the OrdinalIgnoreCase comparer, which will group our characters up.由于您想先获取大写字母,我们将使用OrdinalIgnoreCase比较器,它将我们的字符分组。

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

Okay, so we've now grouped our characters.好的,现在我们已经对我们的角色进行了分组。 We'll now want to sort those groups ordinally to get the capitals to come first, using ThenBy.我们现在要使用 ThenBy 按顺序对这些组进行排序,以使大写字母排在最前面。

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

Finally, we can turn it into an array.最后,我们可以把它变成一个数组。

   .ToArray();

Bringing it together, we get the following:把它放在一起,我们得到以下内容:

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

Which gives us an array that looks like this: ["A","a","B","b","C","c"]这给了我们一个看起来像这样的数组: ["A","a","B","b","C","c"]

At this point, as @dvo has pointed out, we can turn it back into a single string with a call to String.Join, like so:此时,正如@dvo 所指出的,我们可以通过调用 String.Join 将其转换回单个字符串,如下所示:

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

As @RufusL points out, we can do more for this query.正如@RufusL 指出的那样,我们可以为这个查询做更多的事情。 Since we know we ultimately want a string, we can skip the ToArray call entirely and stick with the IOrderedEnumerable<string> .因为我们知道我们最终想要一个字符串,所以我们可以完全跳过ToArray调用并坚持使用IOrderedEnumerable<string>

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

Since we aren't joining anything with strings we can use string.Concat instead of string.Join .因为我们没有用字符串连接任何东西,所以我们可以使用string.Concat而不是string.Join

var resStr = string.Concat(res);

The problem is that your code is ordering by the ASCII value, which is 65-90 for uppercase ( AZ ) and 97-122 for lowercase ( az ).问题是您的代码按 ASCII 值排序,大写 ( AZ ) 为65-90 ,小写 ( az ) 为97-122

Asuming you always want the uppercase letters to come before their lowercase counterparts, we can use a little trick to sort the lowercase letters just after their uppercase value by subtracting 31.5 from the ASCII value before doing the comparison.假设您总是希望大写字母出现在小写字母之前,我们可以使用一个小技巧,通过在进行比较之前从 ASCII 值中减去31.5 ,将小写字母排在大写值之后。

By doing this, a becomes 65.5 ( 97 - 31.5 ) and will be sorted between A ( 65 ) and B ( 66 ).通过这样做, a变为65.5 ( 97 - 31.5 ) 并将在A ( 65 ) 和B ( 66 ) 之间排序。 Likewise for all the other lower-case letters.对于所有其他小写字母也是如此。 This avoids the cost of creating a bunch of new strings for comparison and doing multiple orderings.这避免了创建一堆新字符串进行比较和进行多次排序的成本。

For example:例如:

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

We can also use char.IsUpper and char.ToLower instead of hard-coded ascii values:我们还可以使用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));

Output:输出:

在此处输入图片说明


Edit编辑

To answer one of the comments, if you want to use this same method to sort the characters such that the lower-case letters come before their upper-case counterparts, you would then subtract 32.5 if the character is in the upper-case range.要回答其中一条评论,如果您想使用相同的方法对字符进行排序,使小写字母排大写字母之前,那么如果该字符在大写范围内,您将减去32.5 This way an a ( 97 ) becomes 64.5 ( 97 - 32.5 ) and will be sorted before A ( 65 ):这样a ( 97 ) 变成64.5 ( 97 - 32.5 ) 并且将在A ( 65 ) 之前排序:

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

Or using the trick with char.IsLower and char.ToUpper instead of hard-coded ascii values:或者使用带有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));

There's also a solution that doesn't require doing math with the ASCII values: we can first order the characters so that the upper and lower case of each character are next to each other by using OrderBy(char.ToLower) (which sorts by the ASCII value of the character in lower case), and then we can sort them by char.IsLower , which will then sort the lower case characters after the upper case characters:还有一个不需要对 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