You can build a comparator ( IComparer
) that compares strings containing numbers, and passes it to the OrderBy
method:
lista.OrderBy(c => c.Str, meuComparador)
This comparator you can do using Regex.Split
to divide the string into the positions where numbers are found:
Regex.Split(str, @"(\d+)")
-
Regex \d+
is to indicate that we want to find strings with 1 or + digits :
-
\d
means any digit
-
+
means find one or more of the previous item
-
The parenthesis around \d+
, serves to indicate to split, that the number must be kept in the array of string distribution, so that we can use it in the comparison. Here's how it's different:
Regex.Split("a123b", @"\d+") => array ["a", "b"]
Regex.Split("a123b", @"(\d+)") => array ["a", "123", "b"]
The class comparator of strings containing numbers
Implemented the class, to be present for whoever needs it in the future. = D
public class ComparerStringComNumeros : IComparer<string>
{
public static ComparerStringComNumeros Instancia
= new ComparerStringComNumeros();
private ComparerStringComNumeros() { }
public int Compare(string x, string y)
{
var itemsA = Regex.Split(x, @"(\d+)");
var itemsB = Regex.Split(y, @"(\d+)");
for (int it = 0; ; it++)
{
if (it == itemsA.Length)
return it == itemsB.Length ? 0 : -1;
if (it == itemsB.Length)
return 1;
if ((it % 2) == 0)
{
// parte não numérica
var strCompare = StringComparer.CurrentCulture.Compare(
itemsA[it],
itemsB[it]);
if (strCompare != 0)
return strCompare;
}
else
{
// parte numérica
var numCompare = Comparer<int>.Default.Compare(
int.Parse(itemsA[it]),
int.Parse(itemsB[it]));
if (numCompare != 0)
return numCompare;
}
}
}
}
Test the above class using OrderBy
:
public void TesteDeOrdenacao()
{
var l = new[]
{
"x0.2",
"m1.2",
"m1.04",
"m10.0",
"x1.2",
"x1.04",
"m10.0.0",
"x1.2.2",
"x1.04.8 a",
"x1.04.8 b",
"x1.04.8 c2",
"x1.04.8 c3",
"x1.04.8 c1",
"x10.0",
"m0.2"
};
var l2 = l.OrderBy(x => x, ComparerStringComNumeros.Instancia).ToList();
// l2 irá conter:
//
// "m0.2",
// "m1.2",
// "m1.04",
// "m10.0",
// "m10.0.0",
// "x0.2",
// "x1.2",
// "x1.2.2",
// "x1.04",
// "x1.04.8 a",
// "x1.04.8 b",
// "x1.04.8 c1",
// "x1.04.8 c2",
// "x1.04.8 c3",
// "x10.0"
}
How to use it in your code:
var dirs = parentdir.GetDirectories()
.OrderBy(c => c.Name, ComparerStringComNumeros.Instancia)
.ToList();