'MAX and MIN on string array in c#

I have started to learn Linq recently. I came across a few inbuild methods like Min() and Max(). The working of these two methods is fine with int[]. But when it comes to string[], I am curious how it will work. I have tried some codes

 string[] cars = { "Volvo", "BMW", "Ford", "Mazda" };
            Console.WriteLine(cars.Max());
            Console.WriteLine(cars.Min());

The output was like this:

**Volvo for Max()

BMW for Min()**

Can you please explain how it is working, is it taking the first letter in alphabetical order or is there any mechanism it is using like based on ASCII values etc?



Solution 1:[1]

All types that implement the IComparable or IComparable interface can be compared, using the CompareTo method implemented by each type. All primitive types implement IComparable<T>, including char and string. LINQ's Min(IEnumerable) and Max(IEnumerable) use this implementation to find the minimum or maximum in an enumerable.

String Comparisons

Comparing strings though is a bit more interesting than comparing integers. The strings are typically compared in dictionary order (lexicographically) but ... whose dictionary? Different languages have different sorting rules, and sometimes two letters are considered a single one. Even Danes forget that AA is equivalent to Å in Danish.

The dictionary used to compare strings is provided by the CultureInfo class. By default, the current thread's culture is used which typically matches the culture of the end user (in desktop applications) or the system locale in server applications. In a Danish culture for example, AA is treated differently from aa - I think one of them is ordered after other letters of the same case and the other isn't, but don't ask me which.

The InvariantCulture specifies a locale-insensitive culture that can be used to handle strings the same way in every locale. It uses mostly sensible settings (eg . for the decimal point) except dates, where it uses the US format instead of the ISO8601 (YYYY-MM-DD) format as everyone would expect.

Custom comparisons

It's possible to specify a different comparison method by passing a class that implements IComparer to any LINQ methods affected by order. Min(IEnumerable,IComparer) is one example.

The StringComparer class contains some predefined comparers :

  • CurrentCulture is the default
  • CurrentCultureIgnoreCase uses the current culture but ignores case, so A is equal to a. This is very useful eg in dictionaries.
  • InvariantCulture and InvariantCultureIgnoreCase use the Invariant culture for ordering
  • Finally, Ordinal and OrdinalIgnoreCase don't use a dictionary but compare the Unicode values of the characters. That's the fastest option if you don't care about locale rules

Solution 2:[2]

.Max() use Compare(String, String) which compares two specified String objects and returns an integer that indicates their relative position in the sort order.

Source code of .Max() for string compare

 public static TSource Max<TSource>(this IEnumerable<TSource> source) {
            if (source == null) throw Error.ArgumentNull("source");
            Comparer<TSource> comparer = Comparer<TSource>.Default;
            TSource value = default(TSource);
            if (value == null) {
                foreach (TSource x in source) {
                    if (x != null && (value == null || comparer.Compare(x, value) > 0))
                        value = x;
                }
                return value;
            }
            else {
                bool hasValue = false;
                foreach (TSource x in source) {
                    if (hasValue) {
                        if (comparer.Compare(x, value) > 0) //Compare strings
                            value = x;
                    }
                    else {
                        value = x;
                        hasValue = true;
                    }
                }
                if (hasValue) return value;
                throw Error.NoElements();
            }
        }

Compare(String, String) https://docs.microsoft.com/en-us/dotnet/api/system.string.compare?view=net-6.0#system-string-compare(system-string-system-string)

Solution 3:[3]

Strings are compared alphabetically, which translates into the following logic:

  • which item has a higher first character
  • which item has a higher second character
  • which item has a higher third character
  • ...

The difference is detected at the first character that differs. So, when you compare string1 with string2, then string1 is greater than string2 if at their first differing character (from left to right), string1 has a greater value at that position than string2. If string2 is string1 + string3, then the first difference between string1 and string2 is beyond the end of string1 and at that point the comparison yields that string2 is greater than string1.

If you are dissatisfied with this comparison, then you can specify what comparer you intend to use, see here: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.max?view=net-6.0#system-linq-enumerable-max-1(system-collections-generic-ienumerable((-0))-system-collections-generic-icomparer((-0)))

Basically in that case you need to implement an IComparer and pass it as a second parameter, like

cars.Max(c => c, yourcomparer)

Solution 4:[4]

With string list Min() or Max() follows the first and last word or letter respectively but in case of integers Min() or Max() follows the exact phenomena of finding the Minimum and Maximum numbers from the list. Please check this image and it's output

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Panagiotis Kanavos
Solution 2
Solution 3 Lajos Arpad
Solution 4