3

Suppose I have a list of multi-part questions and each question has a QuestionNumber like 1, 1a,1b,2,2a and so on. I want to fetch a list of questions from the database using linq-to-entities, but ordered by QuestionNumber. The problem is that rather than using the correct order, it will use lexicographic ordering like

1
11
11a
11b
1a
1b
2
22

What I have so far is a custom comparer:

public class QuestionCompare : IComparer<Question>
{
    public int Compare(Question x, Question y)
    {
        string a = x.QuestionNumber;
        string b = y.QuestionNumber;

        if (a == b)
        {
            return 0;
        }

        int aInt;
        bool aBool = Int32.TryParse(new String(a.Where(Char.IsDigit).ToArray()), out aInt);
        int bInt;
        bool bBool = Int32.TryParse(new String(b.Where(Char.IsDigit).ToArray()), out bInt);
        if (aBool)
        {
            if (bBool)
            {
                if (aInt > bInt)
                {
                    return 1;
                }
                else if (aInt < bInt)
                {
                    return -1;
                }
                else
                {
                    string aLetter = new String(a.Where(Char.IsLetter).ToArray());
                    string bLetter = new String(a.Where(Char.IsLetter).ToArray());
                    return StringComparer.CurrentCulture.Compare(aLetter, bLetter);
                }
            }
            else
            {
                return 1;
            }
        }
        else
        {
            if (bBool)
            {
                return -1;
            }
            else
            {
                return StringComparer.CurrentCulture.Compare(a, b);
            }
        }

        return 0;
    }
}

And you can call Array.Sort(questionArray,new QuestionCompare()) to put the questions in the correct order.

However, I feel like this is a common and well defined order so I'm wondering if there are better implementations, perhaps even something built in to the .Net framework.

Matthew
  • 4,149
  • 2
  • 26
  • 53
  • That's natural sorting, so if you use a SortedList per example it will be sorted that way, or even using the Array.Sort(array) overload – Gusman Jan 06 '16 at 19:15
  • @Gusman I think the OP realizes that. Which is the reason for the question and custom comparer. – ryanyuyu Jan 06 '16 at 19:16
  • Read his last statement: *"However, I feel like this is a common and well defined order so I'm wondering if there are better implementations, perhaps even something built in to the .Net framework."* – Gusman Jan 06 '16 at 19:16
  • See: http://stackoverflow.com/questions/248603/natural-sort-order-in-c-sharp – Ani Jan 06 '16 at 19:19
  • @Mattew this is very easy actually and I have found a solution for you. I have voted to re-open the post. – MethodMan Jan 06 '16 at 19:27
  • I like the CompareTo() method instead of the IComparer<> method. – jdweng Jan 06 '16 at 20:22

1 Answers1

1

This comparer works fine and is a fair bit shorter.

public class QuestionCompare : IComparer<Question>
{
    public int Compare(Question x, Question y)
    {
        string a = x.QuestionNumber;
        string b = y.QuestionNumber;

        var aDigits = new string(a.TakeWhile(c => char.IsDigit(c)).ToArray());
        var bDigits = new string(b.TakeWhile(c => char.IsDigit(c)).ToArray());

        int aInt = String.IsNullOrEmpty(aDigits) ? 0 : int.Parse(aDigits);
        int bInt = String.IsNullOrEmpty(bDigits) ? 0 : int.Parse(bDigits);

        return aInt != bInt ? aInt.CompareTo(bInt) : a.CompareTo(b);
    }
}
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • nice. A bit of testing suggests your code is also slightly faster. It does do something weird if one of the items has no digits--this comes after 0 but before 1 for some reason. – Matthew Jan 07 '16 at 14:50
  • I got the impression that there was always a number followed by and optional letter, and that there would not be a question `0`. You could change the default zeros to `-1` or `int.MaxValue` and see if they behave as you expect. – Enigmativity Jan 08 '16 at 00:20