-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API proposal: Comparison by key #33877
Comments
I definitely like this idea, though I think it becomes much more valuable if you add the ability to compare multiple keys. That would mean instead of: people.Sort((x, y) =>
{
int result = x.LastName.CompareTo(y.LastName);
if (result == 0)
result = x.FirstName.CompareTo(y.FirstName);
if (result == 0)
result = x.MiddleName.CompareTo(y.MiddleName);
return result;
}); you could write something like: people.Sort(KeyComparer<Person>.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName)
.ThenBy(p => p.MiddleName)); A NuGet package that implements this already exists, though, to my dismay, it's not very popular. |
@svick One thing I've been favoring even with LINQ instead of ThenBy is to select a tuple: people.Sort(Comparer<Person>.By(p => (p.LastName, p.FirstName, p.MiddleName))); The only thing this doesn't let you do is specify a comparer for each tuple element, but that's a proposal I have on my list to file separately from this because I want it without selectors too. |
Tagging subscribers to this area: @eiriktsarpalis, @jeffhandley Issue DetailsBeforeYou have to write out the selector twice: names.Sort((first, second) => string.Compare(
first.Identifier.GetIdentifierText(),
second.Identifier.GetIdentifierText(),
StringComparer.OrdinalIgnoreCase)); ( statements.OrderBy(GetNameSegments, Comparer.Sequence(
Comparer.Create((first, second) => string.Compare(
first.Identifier.GetIdentifierText(),
second.Identifier.GetIdentifierText(),
StringComparer.OrdinalIgnoreCase))) After// Proposal 1
names.Sort(Comparer.By(
(SimpleNameSyntax name) => name.Identifier.GetIdentifierText(),
StringComparer.OrdinalIgnoreCase));
// Proposal 2
names.Sort(Comparer<SimpleNameSyntax>.By(
name => name.Identifier.GetIdentifierText(),
StringComparer.OrdinalIgnoreCase)) // Proposal 1
statements.OrderBy(GetNameSegments, Comparer.Sequence(Comparer.By(
(SimpleNameSyntax name) => name.Identifier.GetIdentifierText(),
StringComparer.OrdinalIgnoreCase));
// Proposal 2
statements.OrderBy(GetNameSegments, Comparer.Sequence(Comparer<SimpleNameSyntax>.By(
name => name.Identifier.GetIdentifierText(),
StringComparer.OrdinalIgnoreCase)) Proposal 1namespace System.Collections
{
public sealed class Comparer : IComparer, ISerializable
{
+ public static IComparer<T> By<T, TKey>(
+ Func<T, TKey> keySelector,
+ IComparer<TKey>? keyComparer = null);
// No other static members are declared by Comparer
}
} Proposal 2namespace System.Collections.Generic
{
public abstract partial class Comparer<T> : IComparer, IComparer<T>
{
+ public static IComparer<T> By<TKey>(
+ Func<T, TKey> keySelector,
+ IComparer<TKey>? keyComparer = null);
// Other static members declared by Comparer:
public static Comparer<T> Create(Comparison<T> comparison);
}
} Draft implementationClick to expandprivate sealed class KeyComparer<T, TKey> : IComparer<T>
{
private readonly Func<T, TKey> keySelector;
private readonly IComparer<TKey> keyComparer;
public KeyComparer(Func<T, TKey> keySelector, IComparer<TKey> keyComparer)
{
this.keySelector = keySelector;
this.keyComparer = keyComparer;
}
public int Compare([AllowNull] T x, [AllowNull] T y)
{
return keyComparer.Compare(
keySelector.Invoke(x!),
keySelector.Invoke(y!));
}
}
|
I would probably prefer proposal 2, since the untyped Comparer type lives in the |
A similar API for |
FWIW this feels like it could be an overload for Comparer.Create, e.g. public class Comparer<T>
{
public static IComparer<T> Create<TKey>(Func<T, TKey> keySelector);
public static IComparer<T> Create<TKey>(Func<T, TKey> keySelector, IComparer<TKey>? keyComparer);
} |
I think we might need to submit an API proposal that merges this with #33873 so that we can land on a holistic design. |
@eiriktsarpalis I also want to propose a value tuple comparer so that it's possible to e.g. specify OrdinalIgnoreCase for
|
Go for it. I would recommend creating a fresh issue using the API proposal template so we can start from scratch. Please also check if there are other related issues in the backlog so we can consider including them in the design. |
@eiriktsarpalis I'm not sure, what's been done before that's similar? |
I would probably look at the static APIs in the |
Before
You have to write out the selector twice:
(
Comparer.Sequence
is #33873)After
Proposal 1
Proposal 2
Draft implementation
Click to expand
The text was updated successfully, but these errors were encountered: