Search code examples
c#sortingicomparable

Custom common number-text comparer in C# to sort numeric/string list?


I want to create a similar number-text comparer to the one described in How to create custom common number-text comparer in C# to sort numeric/string list?.

public class NumberTextComparer : IComparer<string>
    {
        public int Compare(string s1, string s2)
        {
            double number1, number2;
            var isS1Numeric = double.TryParse(s1, out number1);
            var isS2Numeric = double.TryParse(s2, out number2);

            if (isS1Numeric && isS2Numeric)
            {
                if (number1 > number2) return 1;
                if (number1 < number2) return -1;
                return 0;
            }
            if (isS1Numeric)
                return 1;
            if (isS2Numeric)
                return -1;

            bool s1StartsWithLetter = char.IsLetter(s1.FirstOrDefault());
            bool s2StartsWithLetter = char.IsLetter(s2.FirstOrDefault());

            if (s1StartsWithLetter == s2StartsWithLetter)
                return String.Compare(s1, s2, StringComparison.OrdinalIgnoreCase);
            if (s1StartsWithLetter)
                return -1;
            return 1;
        }
    }

var numericList = new List<string>{"100", "--", "-0.98", "N/A", "0.00", "-888"};
var stringList = new List<string> {"Smith", "--", "Peter", "", "Jim", "Ken", "NA"};

Console.WriteLine(String.Join(", ", numericList.OrderBy(v => v, new NumberTextComparer())));
Console.WriteLine(String.Join(", ", stringList.OrderBy(v => v, new NumberTextComparer())));

Which gives following output:

  1. N/A, --, -888, -0.98, 0.00, 100
  2. Jim, Ken, NA, Peter, Smith, , --

But I want in the string sorting: "" or -- should come first instead of last in ascending order. Numeric sort is as excepted.

So the expected result will be:

  1. Numeric Sort Asc -> N/A, --,-888, -0.98, 0.00, 100
  2. String Sort Asc -> , --, Jim, Ken, NA, Peter, Smith

Is it possible to achieve this?


Solution

  • As per my understanding you require 2 different sort behavior in case of Number and String data. So you can achieve this by passing required type as below-

     public enum SortColumnType
     {
            Numeric=1,
            String=2
     }
    
    public class NumberTextComparer : IComparer<string>
        {
            public SortColumnType ColumnType { get; set; }
    
            public NumberTextComparer(SortColumnType columnType)
            {
                ColumnType = columnType;
            }
    
            private int CustomNumberSort(string s1, string s2)
            {
                double number1, number2;
                var isS1Numeric = double.TryParse(s1, out number1);
                var isS2Numeric = double.TryParse(s2, out number2);
    
                if (isS1Numeric && isS2Numeric)
                {
                    if (number1 > number2) return 1;
                    if (number1 < number2) return -1;
                    return 0;
                }
                if (isS1Numeric)
                    return 1;
                if (isS2Numeric)
                    return -1;
    
                var s1StartsWithLetter = !string.IsNullOrEmpty(s1) && char.IsLetterOrDigit(s1[0]);
                var s2StartsWithLetter = !string.IsNullOrEmpty(s2) && char.IsLetterOrDigit(s2[0]);
    
                if (s1StartsWithLetter == s2StartsWithLetter)
                    return String.Compare(s1, s2, StringComparison.OrdinalIgnoreCase);
                if (s1StartsWithLetter)
                    return -1;
                return 1;
            }
    
            public int Compare(string s1, string s2)
            {
                return ColumnType.Equals(SortColumnType.Numeric)
                    ? CustomNumberSort(s1, s2)
                    : String.Compare(s1, s2, StringComparison.OrdinalIgnoreCase);
            }
        }
    
    stringList.OrderBy(str => str, new NumberTextComparer(SortColumnType.String));
    numericList.OrderBy(str => str, new NumberTextComparer(SortColumnType.Numeric));