Custom IComparer implementation <> (with an example)

Ive just written the following code that will sort the strings by their native string.Compare() , but allow a set of exceptions (in this case customPriority ) that will put the default priority of string.Compare() .

Everything seems a little long, I was wondering if there is anything in .NET for this?

  var unorderered = new[] { "a", "b", "c", "x", "y", "z" }; var ordered = unorderered.OrderBy(a => a, new CustomStringComparer()); //expected order y,x,a,b,c,z class CustomStringComparer : IComparer<string> { int IComparer<string>.Compare(string x, string y) { if (x == y) return 0; else { //---------------------------- //beginning of custom ordering var customPriority = new[] { "y", "x" }; if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array { if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y)) return -1; return 1; } else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x) return -1; else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y) return 1; //--------------------------- //degrade to default ordering else return string.Compare(x, y); } } } 
+4
source share
3 answers

Firstly, I think it is useful to reformulate the problem: you want to sort by:

  • index in this array; if the item is not in the array, the index is infinite
  • the string itself

This means that you can execute your sort order using OrderBy() for the first condition, followed by ThenBy() for the second:

 private static uint NegativeToMaxValue(int i) { if (i < 0) return uint.MaxValue; return (uint)i; } … var ordered = unorderered .OrderBy(a => NegativeToMaxValue(Array.IndexOf(new[] { "y", "x" }, a))) .ThenBy(a => a); 

NegativeToMaxValue() necessary because elements that are not in the array must be the last, but they will be the first, since the index is -1. (A hacky and impenetrable way to do the same would be to directly pass the result of IndexOf() to uint .)

If you want to reuse this sorting by creating IComparer , I believe that there is nothing in .NET to help you with this. But instead, you can use ComparerExtensions :

 IComparer<string> comparer = KeyComparer<string> .OrderBy(a => NegativeToMaxValue(Array.IndexOf(new[] { "y", "x" }, a))) .ThenBy(a => a); 
+3
source

There is no built-in comparison method to do what you want, but I assume this is not the "long branch" that you are talking about.

What's annoying is that you need to create your own comparison class in order to pass what should be a simple comparison function.

Well, there is a way to mitigate this. You can write a couple of helper classes that let you use OrderBy () just by passing in the method name. If you write these classes, they will work for ALL of your OrderBy () statements.

Here is a sample code. Helper classes are called EnumerableExt and ComparisonDelegator. They work together so you can pass the OrderBy () method.

The code below is clearly much longer than your code, but remember that the EnumerableExt and ComparisonDelegator classes would be in a separate common assembly, so you should not consider them.

 using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; namespace Demo { public static class Program { private static void Main(string[] args) { var unorderered = new[] { "a", "b", "c", "x", "y", "z" }; var ordered = unorderered.OrderBy(compare); // Just need to specify the compare method! } // Each custom compare method must be written specially, as before: private static int compare(string x, string y) { if (x == y) return 0; else { //---------------------------- //beginning of custom ordering var customPriority = new[] { "y", "x" }; if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array { if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y)) return -1; return 1; } else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x) return -1; else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y) return 1; //--------------------------- //degrade to default ordering else return string.Compare(x, y); } } } // The following classes only need to be written once: public static class EnumerableExt { /// <summary> /// Convenience method on IEnumerable{T} to allow passing of a /// Comparison{T} delegate to the OrderBy method. /// </summary> public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison) { Contract.Requires(list != null, "list can't be null."); Contract.Requires(comparison != null, "comparer can't be null."); return list.OrderBy(t => t, new ComparisonDelegator<T>(comparison)); } } /// <summary> /// Provides a mechanism for easily converting a Comparison&lt;&gt; delegate (or lambda) to an IComparer&lt;&gt;. /// This can be used for List.BinarySearch(), for example. /// </summary> /// <typeparam name="T">The type of items to be compared.</typeparam> public sealed class ComparisonDelegator<T>: IComparer<T>, IComparer { /// <summary>Create from a Comparison&lt;&gt; delegate.</summary> /// <param name="comparison">A Comparison&lt;&gt; delegate.</param> public ComparisonDelegator(Comparison<T> comparison) { Contract.Requires(comparison != null); this._comparison = comparison; } /// <summary>Implements the IComparer.Compare() method.</summary> public int Compare(T x, T y) { return _comparison(x, y); } /// <summary>Implements the IComparer.Compare() method.</summary> public int Compare(object x, object y) { return _comparison((T)x, (T)y); } /// <summary>Used to store the Comparison delegate.</summary> private readonly Comparison<T> _comparison; } } 

Then you can also write the inline comparison method as follows (but I would not recommend this for such a complicated comparison method, this is for illustration purposes only):

  private static void Main(string[] args) { var unorderered = new[] { "a", "b", "c", "x", "y", "z" }; var ordered = unorderered.OrderBy((x, y) => { if (x == y) return 0; else { var customPriority = new[] { "y", "x" }; if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array { if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y)) return -1; return 1; } else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x) return -1; else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y) return 1; else return string.Compare(x, y); } }); } 
+1
source

I am sure that 99.99% does not exist by default in the .Net Framework.


Your sorting is very customizable and is not a general sorting method, so such things do not exist in the .NET Framework by default.

0
source

All Articles