We all had the issue with an OrderBy on a collection of String, having that kind of result:
Test1
Test10
Test2
Test20
Test3
Instead of:
Test1
Test2
Test3
Test10
Test20
Here is the solution:
public class NaturalComparer : Comparer<string> { public override int Compare(string x, string y) { if (x == y) return 0; string[] x1, y1; x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)"); y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)"); for (int i = 0; i < x1.Length && i < y1.Length; i++) if (x1[i] != y1[i]) return PartCompare(x1[i], y1[i]); if (y1.Length > x1.Length) return 1; else if (x1.Length > y1.Length) return -1; else return 0; } private static int PartCompare(string left, string right) { int x, y; if (int.TryParse(left, out x) && int.TryParse(right, out y)) return x.CompareTo(y); return left.CompareTo(right); } } public class ReverseNaturalComparer : NaturalComparer { public override int Compare(string x, string y) { return base.Compare(y, x); } }
And this is how to use it:
[TestClass] public class NaturalComparer_Test { public List<string> _unsortedCollection { get { var stringCollection = new List<string>(); stringCollection.Add("Test10"); stringCollection.Add("Test3"); stringCollection.Add("Test1"); stringCollection.Add("Test20"); stringCollection.Add("Test2"); return stringCollection; } private set { } } [TestMethod] public void NaturalSort_Test() { var sortedCollection = _unsortedCollection .OrderBy(x => x, new NaturalComparer()) .ToList(); Assert.IsNotNull(sortedCollection); Assert.IsTrue(sortedCollection.Last() == "Test20"); } [TestMethod] public void ReversNaturalSort_Test() { var sortedCollection = _unsortedCollection .OrderBy(x => x, new ReverseNaturalComparer()) .ToList(); Assert.IsNotNull(sortedCollection); Assert.IsTrue(sortedCollection.Last() == "Test1"); } }
Happy coding! 😉