Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/stephencleary/comparers

The last comparison library you'll ever need!
https://github.com/stephencleary/comparers

c-sharp comparer comparers comparison comparison-library dotnet equality equality-comparers icomparable icomparer iequalitycomparer sort

Last synced: 4 days ago
JSON representation

The last comparison library you'll ever need!

Awesome Lists containing this project

README

        

![Logo](src/icon.png)

# Comparers [![Build status](https://github.com/StephenCleary/Comparers/workflows/Build/badge.svg)](https://github.com/StephenCleary/Comparers/actions?query=workflow%3ABuild) [![codecov](https://codecov.io/gh/StephenCleary/Comparers/branch/main/graph/badge.svg)](https://codecov.io/gh/StephenCleary/Comparers) [![NuGet version](https://badge.fury.io/nu/Nito.Comparers.svg)](https://www.nuget.org/packages/Nito.Comparers) [![API docs](https://img.shields.io/badge/API-FuGet-blue.svg)](https://www.fuget.org/packages/Nito.Comparers)

The last comparison library you'll ever need! Wide platform support; fluent syntax.

## Creating Comparers

Install the [`Nito.Comparers` NuGet package](https://www.nuget.org/packages/Nito.Comparers). By default, this includes the [extension package for LINQ](https://www.nuget.org/packages/Nito.Comparers.Linq) support.

The comparer types are in the namespace `Nito.Comparers`.

Let's say you've got a collection of your POCOs:

```c#
class Person
{
public string FirstName { get; }
public string LastName { get; }
}
List list = ...;
```

Here's an easy way to sort them all by last name and then first name:

```c#
IComparer nameComparer =
ComparerBuilder.For()
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName);
list.Sort(nameComparer);
```

### Implementing Comparable Types

How about having Person implement it?
Let's face it: implementing comparison in .NET is a real pain. `IComparable`, `IComparable`, `IEquatable`, `Object.Equals`, *and* `Object.GetHashCode`?!?!
But it's easy with a base type:

```c#
class Person : ComparableBase
{
static Person()
{
DefaultComparer =
ComparerBuilder.For()
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName);
}

public string FirstName { get; }
public string LastName { get; }
}
```

`ComparableBase` auto-magically implements all the comparable interfaces, including correct overrides of `Object.Equals` and `Object.GetHashCode`.

### Using Comparers in Hash Containers

What about hash-based containers? Every single comparer produced by the Comparers library also implements equality comparison!

```c#
IEqualityComparer nameComparer =
ComparerBuilder.For()
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName);
Dictionary dict = new Dictionary(nameComparer);
```

### Equality Comparers

Sometimes, you can only define equality. Well, good news: there are equality comparer types that parallel the full comparer types.

```c#
class Entity : EquatableBase
{
static Entity()
{
DefaultComparer =
EqualityComparerBuilder.For()
.EquateBy(e => e.Id);
}

public int Id { get; }
}
```

### Working with Sequences

Sequences are sorted lexicographically. The `Sequence` operator takes an existing comparer for one type, and defines a lexicographical comparer for sequences of that type:

```c#
var nameComparer =
ComparerBuilder.For()
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName);
List> 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):

```c#
IEnumerable people = ...;
var anonymousProjection = people.Select(x => new { GivenName = x.FirstName, Surname = x.LastName });
var reduced = anonymousProjection.Distinct(c => c.EquateBy(x => x.Surname));
```

### Dynamic Sorting

Need to sort dynamically at runtime? No problem!

```c#
var sortByProperties = new[] { "LastName", "FirstName" };
IComparer comparer = ComparerBuilder.For().Null();
foreach (var propertyName in sortByProperties)
{
var localPropertyName = propertyName;
Func selector = p => p.GetType().GetProperty(localPropertyName).GetValue(p, null) as string;
comparer = comparer.ThenBy(selector);
}
```

### Complex Sorting

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:

```c#
// 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()
.OrderBy(p => p.LastName == "Smith", descending: true)
.ThenBy(ComparerBuilder.For().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., `null`s will be last in a sorted collection):

```c#
List myInts = ...;
var comparer =
ComparerBuilder.For()
.OrderBy(i => i == null, specialNullHandling: true)
.ThenBy(ComparerBuilder.For().Default());
myInts.Sort(comparer);
// Note: we need to pass "specialNullHandling"; otherwise, the default null-ordering rules will apply.
```

### More?!

For full details, see [the detailed docs](doc).

### What's with the flying saucer?

Other languages provide a comparison operator `<=>`, which is called the "spaceship operator". This library provides similar capabilities for C#, hence the "spaceship logo".