Introduction
This is an alternative to the original tip[^]. While that original tip shows a fully hand-crafted version of sorting an array by a complex sort key, I show here a LINQ based approach, based on the IComparer<T>
interface.
The Complex Sort Criteria
The OP defined quite a complex sort criteria which I try to summarize more formally:
- strings with capital letters come before strings without capital letters
- with the character position of the first capital letter in a string: smaller index comes first
- if same index: sorting is given in lexicographical order[^] of that respective capital letter
- if same capital letter or no capital letter contained in the element string: sorting is given by the original strings, case insensitive
Now that we have the sort criteria, let's look at the code.
The Code
I'd like to call the code as given by the OP, but with the LINQ OrderBy(...)[^] extension method overload that takes a key generator plus an IComparer of such keys.
The application of the code shall be as follows:
public static void Main()
{
string[] data = { "wZa", "wwwWa", "wbwwwa", "wWe", "sSsssssw",
"Aww", "abwqA","aXcd","kolkataKnight","aXcD" };
foreach (var item in data.OrderBy(Cmp.Key, Cmp.Default))
{
Console.WriteLine(item);
}
}
With the OP provided data, the output is
Aww
sSsssssw
wWe
aXcd
aXcD
wZa
wwwWa
abwqA
kolkataKnight
wbwwwa
The Cmp Class
The OrderBy(...)
method takes a key generator delegate plus an IComparer<T>[^] instance for comparing the keys.
Here goes the class:
class Cmp : IComparer<Tuple<int, char, string>>
{
public static readonly Cmp Default = new Cmp();
public static Tuple<int, char, string> Key(string s)
{
int pos = (s.Select((c,i)=>char.IsUpper(c) ? i+1 : 0)
.FirstOrDefault(i=>i>0)
)-1;
return pos < 0
? new Tuple<int, char, string>(int.MaxValue, ' ', s.ToLower())
: new Tuple<int, char, string>(pos, s[pos], s.ToLower());
}
public int Compare(Tuple<int, char, string> x, Tuple<int, char, string> y)
{
int res = x.Item1.CompareTo(y.Item1);
if (res == 0) res = x.Item2.CompareTo(y.Item2);
if (res == 0) res = x.Item3.CompareTo(y.Item3);
return res;
}
}
Key features:
- The key consists of three parts, implemented by a Tuple[^] class.
- The three items of the key represent the three sort criteria: position, capital letter, element string as lower case.
- To achieve the 1st criterion, strings with only lower letters have the "capital letter position" set to
int.MaxValue
, leading to the correct sort constraint for these cases. - The
IComparer<T>
implementation of the Compare
method is straight forward with this key: compare the 1st criterion, if equal go to the 2nd, if that is still equal, take the last one.
Performance considerations are not made, i.e., this complex sorting may not be so suitable for huge data lots, but for reasonably sized collections, it may be sufficient.
History
- V1.0, 2012-06-05: Initial version