0

Actually, another title for the question would be: how to find the character with the highest alphabetical value for the current culture?

Take a look at the following code:

static void Main(string[] args)
{
    var input = new[] { "cote", "côte", "coté", "", "côté" };
    var maxString = new string(new[] { char.MaxValue });

    var byEnUsCulture = input.OrderBy(i => 
        (String.IsNullOrEmpty(i)) ? maxString : i, 
            StringComparer.Create(new CultureInfo("en-US"), false)).ToList();

    var byFrFrCulture = input.OrderBy(i => 
        (String.IsNullOrEmpty(i)) ? maxString : i, 
            StringComparer.Create(new CultureInfo("fr-FR"), false)).ToList();

    var byOrdinal = input.OrderBy(i => 
        (String.IsNullOrEmpty(i)) ? maxString : i, 
            StringComparer.Ordinal).ToList();

    foreach (var words in new[] { byEnUsCulture, byFrFrCulture, byOrdinal })
    {
        foreach (var word in words)
        {
            Console.Write("{0} ", (string.IsNullOrEmpty(word)) ? "xxxx" : word);
        }
        Console.WriteLine();
    }
}

The output of the above is:

xxxx cote coté côte côté
xxxx cote côte coté côté
cote coté côte côté xxxx

But what I'm trying to get is:

cote côte coté côté xxxx

Is it possible to order the words above by the fr-FR culture info and still output the empty (replaced with xxxx in the output) values at the end of the collection, all that just by using OrderBy?

Note: Here's a reference for the expected order.

Alex Filipovici
  • 31,789
  • 6
  • 54
  • 78
  • If you know that the `""` entries are to go on the end, can you not just extract and concatenate them after ordering using `TakeWhile(p => string.IsNullOrEmpty(p)`? – Gusdor Nov 18 '13 at 10:42
  • `cote côte coté côté` I am kind of confused how you can ever get this order... if sorting works on the characters from left to right, the first two indicate that `ô > o`, whereas the second and the third entry indicate that `ô < o`. I may be missing something obvious but I do not see how both could be true at the same time... – oerkelens Nov 18 '13 at 10:42
  • 1
    Indeed, i _was_ missing something, thanks for that link. (I will refrain from any further remarks about French sorting order... but the _last_ accent counts? Well, you live, you learn:) ) – oerkelens Nov 18 '13 at 10:50

2 Answers2

4

I suppose you could create your own StringComparer subclass that always ensures empty strings are sorted last, before deferring to the fr-FR comparer.

AKX
  • 152,115
  • 15
  • 115
  • 172
  • That's a good suggestion, but it would require some extra processing inside the class. I want to know if it's possible to determine the character with the highest alphabetical value, given a specific culture. – Alex Filipovici Nov 18 '13 at 10:42
  • @AlexFilipovici You want extra function without a processing overhead? Good luck with that man! – Gusdor Nov 18 '13 at 10:45
  • @AlexFilipovici I implemented this solution in my answer. Because it now treats empty strings as null strings there is actually less processing going on. – Gusdor Nov 18 '13 at 11:20
0

Implement a custom string comparer that wraps another string comparer. Your custom comparer should always value empty strings above non-empty strings and forward the compare to the wrapped comparer instance only if both strings are non-null. You are essentially applying the default comparer null behavior to empty strings, but now you are inverting the result.

  • Barely any code
  • Works with any string comparer
  • Guarentees your empty strings are sorted to the end
  • Remains as a single sort operation (pretty sure .net uses QSort)

Code sample:

class EmptyToEndComparer: StringComparer
{
    public StringComparer InnerComparer { get; private set; }

    public EmptyToEndComparer(StringComparer innerComparer)
    {
        if (innerComparer == null) throw new ArgumentNullException("innerComparer");
        InnerComparer = innerComparer;
    }
    public override int Compare(string x, string y)
    {
        //Invert the standard behavior on null or empty
        //http://msdn.microsoft.com/en-us/library/x1ea0esc%28v=vs.110%29.aspx

        if (string.IsNullOrEmpty(x) && !string.IsNullOrEmpty(y))
            return 1;
        else if (!string.IsNullOrEmpty(x) && string.IsNullOrEmpty(y))
            return -1;
        else
            return InnerComparer.Compare(x, y);
    }

    public override bool Equals(string x, string y)
    {
        return x == y;
    }

    public override int GetHashCode(string obj)
    {
        return obj.GetHashCode();
    }
}

Use the class like this:

var byEnUsCulture = input.OrderBy(i => i, new EmptyToEndComparer(StringComparer.Create(new CultureInfo("en-US"), false))).ToList();

var byFrFrCulture = input.OrderBy(i => i, new EmptyToEndComparer(StringComparer.Create(new CultureInfo("fr-FR"), false))).ToList();

var byOrdinal = input.OrderBy(i => i, new EmptyToEndComparer(StringComparer.Ordinal)).ToList();

Output from my test:

cote coté côte côté xxxx
cote côte coté côté xxxx
cote coté côte côté xxxx
Gusdor
  • 14,001
  • 2
  • 52
  • 64