2

I need to sort 28 Tuple. Chars are in A-Z, '+', '*'.

The ASCII order is: *, +, A...Z and I want to get this order: A...Z, +, *.

To do that I use this Comparer:

public class AlphabeticPaginationComparer : IComparer<Tuple<char, int>>
{
    public int Compare(Tuple<char, int> a, Tuple<char, int> b)
    {
        // '*' is greater than all letters and '+',
        if (a.Item1 == '*')
        {
            return 1;
        }

        if (b.Item1 == '*')
        {
            return -1;
        }

        // '+' is greater than all letters
        if (a.Item1 == '+')
        {
            return 1;
        }

        if (b.Item1 == '+')
        {
            return -1;
        }

        // Finally we compare letters normally
        return a.Item1 - b.Item1;
    }
}

Here is my code:

IOrderedEnumerable<Tuple<char, int>> orderedList = 
    origList.OrderBy(c => c, new AlphabeticPaginationComparer());

List<Tuple<char, int>> orderedList2 = orderedList.ToList();

This list is sorted

List<Tuple<char, int>> origList = new List<Tuple<char, int>>
{
    Tuple.Create('L', 15),
    Tuple.Create('R', 12),
    Tuple.Create('E', 3),
    Tuple.Create('P', 9),
    Tuple.Create('B', 30),
    Tuple.Create('C', 13),
    Tuple.Create('M', 11),
    Tuple.Create('A', 5),
    Tuple.Create('V', 5),
    Tuple.Create('H', 6),
    Tuple.Create('S', 5),
    Tuple.Create('G', 15),
    Tuple.Create('T', 8),
    Tuple.Create('D', 15),
    Tuple.Create('K', 4),
    Tuple.Create('I', 2),
    Tuple.Create('F', 7),
    Tuple.Create('W', 3),
    Tuple.Create('+', 6),
    Tuple.Create('O', 1),
    Tuple.Create('Z', 1),
    Tuple.Create('J', 0),
    Tuple.Create('N', 0),
    Tuple.Create('Q', 0),
    Tuple.Create('U', 0),
    Tuple.Create('X', 0),
    Tuple.Create('Y', 0),
    Tuple.Create('*', 176)
};

but not this one, the program hangs on .ToList().

List<Tuple<char, int>> origList = new List<Tuple<char, int>>
{
    Tuple.Create('A', 70),
    Tuple.Create('C', 119),
    Tuple.Create('B', 27),
    Tuple.Create('G', 39),
    Tuple.Create('D', 22),
    Tuple.Create('E', 21),
    Tuple.Create('F', 20),
    Tuple.Create('I', 25),
    Tuple.Create('H', 14),
    Tuple.Create('J', 4),
    Tuple.Create('K', 9),
    Tuple.Create('L', 24),
    Tuple.Create('M', 50),
    Tuple.Create('N', 5),
    Tuple.Create('O', 15),
    Tuple.Create('P', 17),
    Tuple.Create('R', 12),
    Tuple.Create('S', 68),
    Tuple.Create('T', 18),
    Tuple.Create('U', 12),
    Tuple.Create('V', 19),
    Tuple.Create('W', 5),
    Tuple.Create('X', 1),
    Tuple.Create('Y', 1),
    Tuple.Create('+', 1),
    Tuple.Create('Q', 0),
    Tuple.Create('Z', 0),
    Tuple.Create('*', 618)
};

I tried with random lists, and this problem seems also random.

Any help will be welcome.

  • 1
    You aren't following the rules of IComparer in that the order must be deterministic. Your implementation isn't deterministic because your logic for handling `+` and `*` doesn't handle when both items are `+` or `*`. This non-determinism can cause an infinite loop in the .NET sorting code. – Ed T Feb 11 '16 at 16:38

1 Answers1

1

What happens when a.Item1 is + and b.Item1 is also +? You need to return 0, not 1 because they are equal.

Here is how you can fix this:

public class AlphabeticPaginationComparer : IComparer<Tuple<char, int>>
{
    public int Compare(Tuple<char, int> a, Tuple<char, int> b)
    {
        if (a.Item1 == b.Item1)
            return 0;

        //...
    }
}
Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
  • Ideally, you are right but these lists come from a GroupBy and there can not be a duplicate on the char. Oups, you are right. I don't understand why my program runs only sometimes. – D. Lucazeau Feb 11 '16 at 16:28
  • @D.Lucazeau, for some reason, LINQ is trying to compare `('+', 1)` with itself (I am not sure why) and it is getting a 1 where it should have got a 0. – Yacoub Massad Feb 11 '16 at 16:33
  • You have fixed my problem. Thanks – D. Lucazeau Feb 11 '16 at 16:34
  • @YacoubMassad the linq sort routines will occasionally compare an item to itself, so even if the collection has unique items you still need to return 0 if the two values are "equal" as you do. – D Stanley Feb 11 '16 at 20:35