{"id":20247019,"url":"https://github.com/stephencleary/comparers","last_synced_at":"2025-05-15T17:09:07.686Z","repository":{"id":16259577,"uuid":"19007658","full_name":"StephenCleary/Comparers","owner":"StephenCleary","description":"The last comparison library you'll ever need!","archived":false,"fork":false,"pushed_at":"2023-12-09T16:14:07.000Z","size":546,"stargazers_count":438,"open_issues_count":7,"forks_count":33,"subscribers_count":28,"default_branch":"main","last_synced_at":"2025-05-15T17:09:03.832Z","etag":null,"topics":["c-sharp","comparer","comparers","comparison","comparison-library","dotnet","equality","equality-comparers","icomparable","icomparer","iequalitycomparer","sort"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StephenCleary.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["StephenCleary"]}},"created_at":"2014-04-21T21:29:22.000Z","updated_at":"2025-04-16T07:08:40.000Z","dependencies_parsed_at":"2024-06-18T13:54:07.043Z","dependency_job_id":null,"html_url":"https://github.com/StephenCleary/Comparers","commit_stats":{"total_commits":265,"total_committers":6,"mean_commits":"44.166666666666664","dds":"0.24528301886792447","last_synced_commit":"48cd202db5d7ea7209cc4248bf6a531d3752f170"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StephenCleary%2FComparers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StephenCleary%2FComparers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StephenCleary%2FComparers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StephenCleary%2FComparers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StephenCleary","download_url":"https://codeload.github.com/StephenCleary/Comparers/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254384989,"owners_count":22062422,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-sharp","comparer","comparers","comparison","comparison-library","dotnet","equality","equality-comparers","icomparable","icomparer","iequalitycomparer","sort"],"created_at":"2024-11-14T09:34:57.119Z","updated_at":"2025-05-15T17:09:04.556Z","avatar_url":"https://github.com/StephenCleary.png","language":"C#","readme":"![Logo](src/icon.png)\n\n# 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)\n\nThe last comparison library you'll ever need! Wide platform support; fluent syntax.\n\n## Creating Comparers\n\nInstall 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.\n\nThe comparer types are in the namespace `Nito.Comparers`.\n\nLet's say you've got a collection of your POCOs:\n\n```c#\nclass Person\n{\n    public string FirstName { get; }\n    public string LastName { get; }\n}\nList\u003cPerson\u003e list = ...;\n```\n\nHere's an easy way to sort them all by last name and then first name:\n\n```c#\nIComparer\u003cPerson\u003e nameComparer =\n    ComparerBuilder.For\u003cPerson\u003e()\n                   .OrderBy(p =\u003e p.LastName)\n                   .ThenBy(p =\u003e p.FirstName);\nlist.Sort(nameComparer);\n```\n\n### Implementing Comparable Types\n\nHow about having Person implement it?\nLet's face it: implementing comparison in .NET is a real pain. `IComparable\u003cT\u003e`, `IComparable`, `IEquatable\u003cT\u003e`, `Object.Equals`, *and* `Object.GetHashCode`?!?!\nBut it's easy with a base type:\n\n```c#\nclass Person : ComparableBase\u003cPerson\u003e\n{\n    static Person()\n    {\n        DefaultComparer =\n            ComparerBuilder.For\u003cPerson\u003e()\n                           .OrderBy(p =\u003e p.LastName)\n                           .ThenBy(p =\u003e p.FirstName);\n    }\n\n    public string FirstName { get; }\n    public string LastName { get; }\n}\n```\n\n`ComparableBase\u003cT\u003e` auto-magically implements all the comparable interfaces, including correct overrides of `Object.Equals` and `Object.GetHashCode`.\n\n### Using Comparers in Hash Containers\n\nWhat about hash-based containers? Every single comparer produced by the Comparers library also implements equality comparison!\n\n```c#\nIEqualityComparer\u003cPerson\u003e nameComparer =\n    ComparerBuilder.For\u003cPerson\u003e()\n                   .OrderBy(p =\u003e p.LastName)\n                   .ThenBy(p =\u003e p.FirstName);\nDictionary\u003cPerson, Address\u003e dict = new Dictionary\u003cPerson, Address\u003e(nameComparer);\n```\n\n### Equality Comparers\n\nSometimes, you can only define equality. Well, good news: there are equality comparer types that parallel the full comparer types.\n\n```c#\nclass Entity : EquatableBase\u003cEntity\u003e\n{\n    static Entity()\n    {\n        DefaultComparer =\n            EqualityComparerBuilder.For\u003cEntity\u003e()\n                                   .EquateBy(e =\u003e e.Id);\n    }\n\n    public int Id { get; }\n}\n```\n\n### Working with Sequences\n\nSequences are sorted lexicographically. The `Sequence` operator takes an existing comparer for one type, and defines a lexicographical comparer for sequences of that type:\n\n```c#\nvar nameComparer =\n    ComparerBuilder.For\u003cPerson\u003e()\n                   .OrderBy(p =\u003e p.LastName)\n                   .ThenBy(p =\u003e p.FirstName);\nList\u003cIEnumerable\u003cPerson\u003e\u003e groups = ...;\ngroups.Sort(nameComparer.Sequence());\n```\n\nThere's also natural extensions for LINQ that allow you to define comparers on-the-fly (particularly useful for anonymous types):\n\n```c#\nIEnumerable\u003cPerson\u003e people = ...;\nvar anonymousProjection = people.Select(x =\u003e new { GivenName = x.FirstName, Surname = x.LastName });\nvar reduced = anonymousProjection.Distinct(c =\u003e c.EquateBy(x =\u003e x.Surname));\n```\n\n### Dynamic Sorting\n\nNeed to sort dynamically at runtime? No problem!\n\n```c#\nvar sortByProperties = new[] { \"LastName\", \"FirstName\" };\nIComparer\u003cPerson\u003e comparer = ComparerBuilder.For\u003cPerson\u003e().Null();\nforeach (var propertyName in sortByProperties)\n{\n    var localPropertyName = propertyName;\n    Func\u003cPerson, string\u003e selector = p =\u003e p.GetType().GetProperty(localPropertyName).GetValue(p, null) as string;\n    comparer = comparer.ThenBy(selector);\n}\n```\n\n### Complex Sorting\n\nWant 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:\n\n```c#\n// Use the default sort order (last name, then first name), EXCEPT all \"Smith\"s move to the head of the line.\nvar comparer =\n    ComparerBuilder.For\u003cPerson\u003e()\n                   .OrderBy(p =\u003e p.LastName == \"Smith\", descending: true)\n                   .ThenBy(ComparerBuilder.For\u003cPerson\u003e().Default());\nlist.Sort(comparer);\n```\n\nBy 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):\n\n```c#\nList\u003cint?\u003e myInts = ...;\nvar comparer =\n    ComparerBuilder.For\u003cint?\u003e()\n                   .OrderBy(i =\u003e i == null, specialNullHandling: true)\n                   .ThenBy(ComparerBuilder.For\u003cint?\u003e().Default());\nmyInts.Sort(comparer);\n// Note: we need to pass \"specialNullHandling\"; otherwise, the default null-ordering rules will apply.\n```\n\n### More?!\n\nFor full details, see [the detailed docs](doc).\n\n### What's with the flying saucer?\n\nOther languages provide a comparison operator `\u003c=\u003e`, which is called the \"spaceship operator\". This library provides similar capabilities for C#, hence the \"spaceship logo\".\n","funding_links":["https://github.com/sponsors/StephenCleary"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstephencleary%2Fcomparers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstephencleary%2Fcomparers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstephencleary%2Fcomparers/lists"}