Reputation: 2456
I want to sort strings case sensitive: so that if one starts with capital "C" then it should be "bigger" (for example) than the one that starts with "c" but also smaller than the one that starts with "d".
For example, sorted list: "a", "A", "chi", "Che", "Chr"
It is written that String comparison methods are case sensitive by default. But it looks like my understanding of "case sensitive" differs from the default one.
Neither of default methods I tried (String.CompareTo
, String.Compare
(with different StringComparison
values)) gives result I want.
Here is the code I've used for testing:
using System;
using System.Collections.Generic;
public class Test
{
public static void Main()
{
var list = new List<String> { "Che", "Chr", "chi", "a", "A" };
// Any other way to sort goes here
list.Sort((s1, s2) => s1.CompareTo(s2));
for (var i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
}
}
Exactly this code gives the result: "a" "A" "Che" "chi" "Chr". So small "c" is standing between to "C"s.
So, the question is: is there any way to achieve the sort order I want (which looks pretty obvious) with any of default methods, without writing my own comparer? Am I missing something?
Upvotes: 4
Views: 1138
Reputation: 12797
I don't see other way than writing own comparer because of non-strait logic in terms of string: a < A < c < C
= 97 < 65 < 99 < 67
.
sealed class CustomComparer : Comparer<string>
{
public override int Compare(string x, string y)
{
for (int i = 0; i < Math.Min(x.Length, y.Length); i++)
{
char xc = x[i];
char yc = y[i];
if (xc == yc)
continue;
char xcLow = char.ToLowerInvariant(xc);
char ycLow = char.ToLowerInvariant(yc);
if (xcLow == ycLow)
return xc < yc ? 1 : -1;
else
return xcLow < ycLow ? -1 : 1;
}
return x.Length.CompareTo(y.Length);
}
}
Usage:
var list = new List<String> { "Che", "Chr", "chi", "a", "A" };
list.Sort(new CustomComparer()); // a, A, chi, Che, Chr
Upvotes: 6
Reputation: 17755
It sounds like you want to sort by first letter (case sensitive) then by the rest of the word (Case-insensitive). If that's the case, I'd use something like the following LINQ:
list.OrderBy (l => l.Substring(0,1)).ThenBy (l => l.ToLower())
Result: a A chi Che Chr
Upvotes: 1
Reputation: 43300
This comparer seems to do what you are looking for..
class MyComparer : IComparer<string>
{
public int Compare(string x, string y)
{
var sets = x.ToCharArray().Zip(y.ToCharArray(),
(LeftChar, RightChar) => new { LeftChar, RightChar });
var charsToCompare = sets.FirstOrDefault(c => c.LeftChar != c.RightChar);
if (charsToCompare == null)
return 0;
var lowers = char.ToLower(charsToCompare.LeftChar).CompareTo(char.ToLower(charsToCompare.RightChar));
if (lowers == 0)
return charsToCompare.RightChar.CompareTo(charsToCompare.LeftChar);
else
return char.ToUpper(charsToCompare.LeftChar).CompareTo(char.ToUpper(charsToCompare.RightChar));
}
}
Tested with
public class Test
{
public static void Main()
{
var list = new List<String> { "Che", "Chr", "chi", "a", "A" };
// Any other way to sort goes here
list.Sort(new MyComparer());
// list.Sort((s1, s2) => s1.CompareTo(s2));
for (var i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
Console.ReadKey();
}
}
Output
a
A
chi
Che
Chr
Upvotes: 0
Reputation: 419
You should use this method for sorting strings
string.Compare
instead of a.CompareTo
.
String.Compare("Foo", "foo", StringComparison.InvariantCulture)
will return true, giving you the sort order you desire.
Applied to your example:
list.Sort((s1, s2) => string.Compare(s1, s2, StringComparison.InvariantCulture));
Upvotes: -1