Natural sorting
I am trying to use the code borrowed from here to sort a collection of a custom object with my application. Generally, sorting works fine until I come across the following lines
D-016.0,
D-016.,
D-016.00,
D-016.000 001,
D-016.000 002,
D-016.000,
D-016.00 003,
D-016.00 002,
D-016. 001,
D-016. 002,
D-016.0 001,
D-016.00 001
which for some very strange reason returns the collection in order
D-016.00,
D-016.000,
D-016.0,
D-016.000 001,
D-016. 001,
D-016.0 001,
D-016.00 001,
D-016.00 002,
D-016.000 002,
D-016. 002,
D-016.00 003,
D-016.
The collection I expect to see will look like this: Windows Explorer will show what should be
D-016. 001,
D-016. 002,
D-016.,
D-016.000 001,
D-016.000 002,
D-016.000,
D-016.00 001,
D-016.00 002,
D-016.00 003,
D-016.00,
D-016.0 001,
D-016.0,
I suspect that files listed as D-016.00, D-016.0 and D-016.000 will get a list of 0s concatenated when parsed to int, which will result in this error. However, I can't figure out exactly how to fix this exactly (too new for these comparison interfaces). Can anyone suggest a solution for this?
Below is the code I am currently using
public class NaturalSorter<T> : IComparer<string>, IDisposable
{
private readonly bool ascending;
private Dictionary<string, string[]> table = new Dictionary<string, string[]>();
public NaturalSorter(bool inAscendingOrder = true)
{
ascending = inAscendingOrder;
}
#region IComparer Members
public int Compare(string[] x, string[] y)
{
throw new NotImplementedException();
}
#endregion
#region IComparer Members
int IComparer<string>.Compare(string x, string y)
{
if (x == y)
return 0;
string[] x1, y1;
if (!table.TryGetValue(x, out x1))
{
x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)");
table.Add(x, x1);
}
if (!table.TryGetValue(y, out y1))
{
y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)");
table.Add(y, y1);
}
int returnVal = 0;
for (int i = 0; i < x1.Length && i < y1.Length; i++)
{
if (x1[i] != y1[i])
{
returnVal = PartCompare(x1[i], y1[i]);
return ascending ? returnVal : -returnVal;
}
}
if (y1.Length > x1.Length)
{
returnVal = 1;
}
else if (x1.Length > y1.Length)
{
returnVal = -1;
}
else
{
returnVal = 0;
}
return ascending ? returnVal : -returnVal;
}
private static int PartCompare(string left, string right)
{
int x, y;
if (!int.TryParse(left, out x))
return left.CompareTo(right);
if (!int.TryParse(right, out y))
return left.CompareTo(right);
return x.CompareTo(y);
}
#endregion
public void Dispose()
{
table.Clear();
table = null;
}
}
source to share
This is the best I could think of:
Replace the division with:
if (!table.TryGetValue(x, out x1))
{
x1 = Regex.Split(x, @"([0-9]+|\.)");
table.Add(x, x1);
}
if (!table.TryGetValue(y, out y1))
{
y1 = Regex.Split(y, @"([0-9]+|\.)");
table.Add(y, y1);
}
And add the following to the beginning PartCompare()
private static int PartCompare(string left, string right)
{
if (left.Length > right.Length) return -1;
else if (left.Length < right.Length) return 1;
int x, y;
...
This gives:
D-016. 001,
D-016. 002,
D-016.000 001,
D-016.000 002,
D-016.000,
D-016.00 001,
D-016.00 002,
D-016.00 003,
D-016.00,
D-016.0 001,
D-016.0,
D-016., // The one out-of-order item.
source to share