Comparers Build status codecov NuGet version API docs
The last comparison library you'll ever need! Wide platform support; fluent syntax.
Install the Nito.Comparers NuGet package. By default, this includes the extension package for LINQ support.
The comparer types are in the namespace Nito.Comparers.
Let's say you've got a collection of your POCOs:
class Person { public string FirstName { get; } public string LastName { get; } } List<Person> list = ...;
Here's an easy way to sort them all by last name and then first name:
IComparer<Person> nameComparer = ComparerBuilder.For<Person>() .OrderBy(p => p.LastName) .ThenBy(p => p.FirstName); list.Sort(nameComparer);
How about having Person implement it?
Let's face it: implementing comparison in .NET is a real pain. IComparable<T>, IComparable, IEquatable<T>, Object.Equals, and Object.GetHashCode?!?!
But it's easy with a base type:
class Person : ComparableBase<Person> { static Person() { DefaultComparer = ComparerBuilder.For<Person>() .OrderBy(p => p.LastName) .ThenBy(p => p.FirstName); } public string FirstName { get; } public string LastName { get; } }
ComparableBase<T> auto-magically implements all the comparable interfaces, including correct overrides of Object.Equals and Object.GetHashCode.
What about hash-based containers? Every single comparer produced by the Comparers library also implements equality comparison!
IEqualityComparer<Person> nameComparer = ComparerBuilder.For<Person>() .OrderBy(p => p.LastName) .ThenBy(p => p.FirstName); Dictionary<Person, Address> dict = new Dictionary<Person, Address>(nameComparer);
Sometimes, you can only define equality. Well, good news: there are equality comparer types that parallel the full comparer types.
class Entity : EquatableBase<Entity> { static Entity() { DefaultComparer = EqualityComparerBuilder.For<Entity>() .EquateBy(e => e.Id); } public int Id { get; } }
Sequences are sorted lexicographically. The Sequence operator takes an existing comparer for one type, and defines a lexicographical comparer for sequences of that type:
var nameComparer = ComparerBuilder.For<Person>() .OrderBy(p => p.LastName) .ThenBy(p => p.FirstName); List<IEnumerable<Person>> groups = ...; groups.Sort(nameComparer.Sequence());
There's also natural extensions for LINQ that allow you to define comparers on-the-fly (particularly useful for anonymous types):
IEnumerable<Person> people = ...; var anonymousProjection = people.Select(x => new { GivenName = x.FirstName, Surname = x.LastName }); var reduced = anonymousProjection.Distinct(c => c.EquateBy(x => x.Surname));
Need to sort dynamically at runtime? No problem!
var sortByProperties = new[] { "LastName", "FirstName" }; IComparer<Person> comparer = ComparerBuilder.For<Person>().Null(); foreach (var propertyName in sortByProperties) { var localPropertyName = propertyName; Func<Person, string> selector = p => p.GetType().GetProperty(localPropertyName).GetValue(p, null) as string; comparer = comparer.ThenBy(selector); }
Want a cute trick? Here's one: true is "greater than" false, so if you want to order by some weird condition, it's not too hard:
// Use the default sort order (last name, then first name), EXCEPT all "Smith"s move to the head of the line. var comparer = ComparerBuilder.For<Person>() .OrderBy(p => p.LastName == "Smith", descending: true) .ThenBy(ComparerBuilder.For<Person>().Default()); list.Sort(comparer);
By default, null values are "less than" anything else, but you can use the same sort of trick to sort them as "greater than" non-null values (i.e., nulls will be last in a sorted collection):
List<int?> myInts = ...; var comparer = ComparerBuilder.For<int?>() .OrderBy(i => i == null, specialNullHandling: true) .ThenBy(ComparerBuilder.For<int?>().Default()); myInts.Sort(comparer); // Note: we need to pass "specialNullHandling"; otherwise, the default null-ordering rules will apply.
For full details, see the detailed docs.
Other languages provide a comparison operator <=>, which is called the "spaceship operator". This library provides similar capabilities for C#, hence the "spaceship logo".