LINQ and CASE Sensitivity

2019-02-25 16:04发布

问题:

I have this LINQ Query:

TempRecordList = new ArrayList(TempRecordList.Cast<string>().OrderBy(s => s.Substring(9, 30)).ToArray());

It works great and performs sorting in a way that's accurate but a little different from what I want. Among the the result of the query I see something like this:

Palm-Bouter, Peter
Palmer-Johnson, Sean

Whereas what I really need is to have names sorted like this:

Palmer-Johnson, Sean
Palm-Bouter, Peter

Basically I want the '-' character to be treated as being lower than the character so that names that contain it show up later in an ascending search.

Here is another example. I get:

Dias, Reginald
DiBlackley, Anton

Instead of:

DiBlackley, Anton
Dias, Reginald

As you can see, again, the order is switched due to how the uppercase letter 'B' is treated.

So my question is, what do I need to change in my LINQ query to make it return results in the order I specified. Any feedback would be greatly appreaciated.

By the way, I tried using s.Substring(9, 30).ToLower() but that didn't help.

Thank you!

回答1:

To customize the sorting order you will need to create a comparer class that implements IComparer<string> interface. The OrderBy() method takes comparer as second parameter.

internal sealed class NameComparer : IComparer<string> {
    private static readonly NameComparer DefaultInstance = new NameComparer();

    static NameComparer() { }
    private NameComparer() { }

    public static NameComparer Default {
        get { return DefaultInstance; }
    }

    public int Compare(string x, string y) {
        int length = Math.Min(x.Length, y.Length);
        for (int i = 0; i < length; ++i) {
            if (x[i] == y[i]) continue;
            if (x[i] == '-') return 1;
            if (y[i] == '-') return -1;
            return x[i].CompareTo(y[i]);
        }

        return x.Length - y.Length;
    }
}

This works at least with the following test cases:

var names = new[] {
    "Palmer-Johnson, Sean",
    "Palm-Bouter, Peter",
    "Dias, Reginald",
    "DiBlackley, Anton",
};

var sorted = names.OrderBy(name => name, NameComparer.Default).ToList();

// sorted:
// [0]: "DiBlackley, Anton"
// [1]: "Dias, Reginald"
// [2]: "Palmer-Johnson, Sean"
// [3]: "Palm-Bouter, Peter"


回答2:

As already mentioned, the OrderBy() method takes a comparer as a second parameter.

For strings, you don't necessarily have to implement an IComparer<string>. You might be fine with System.StringComparer.CurrentCulture (or one of the others in System.StringComparer).

In your exact case, however, there is no built-in comparer which will handle also the - after letter sort order.



回答3:

OrderBy() returns results in ascending order.

e comes before h, thus the first result (remember you're comparing on a substring that starts with the character in the 9th position...not the beginning of the string) and i comes before y, thus the second. Case sensitivity has nothing to do with it.

If you want results in descending order, you should use OrderByDescending():

TempRecordList.Cast<string>
              .OrderByDescending(s => s.Substring(9, 30)).ToArray());


回答4:

You might want to just implement a custom IComparer object that will give a custom priority to special, upper-case and lower-case characters.

http://msdn.microsoft.com/en-us/library/system.collections.icomparer.aspx