Description
There is no .NET API to sort sequences lexicographically. The closest thing I know of is Enumerable.SequenceEqual
which reports elementwise equality but not order.
I've implemented one-off array/enumerable comparers in enough projects that this last time I figured I should propose it.
Usage example
var sortedPaths = path.OrderBy(
path => path.GetSegmentNames(),
Comparer.Sequence(StringComparer.OrdinalIgnoreCase));
Proposal
It should be a generic method on a non-generic type for usability (CA1000).
If an element comparer instance is not specified, Comparer<T>.Default
should be used for consistency with other APIs.
IComparer<>
is contravariant, so the returned comparer will be usable for any sequence of reference types implementing IEnumerable<T>
.
The returned comparer type should likely not be a class that also implements IEqualityComparer because GetHashCode would have to be implemented as 0
if the user-specified element comparer cannot be cast to IEqualityComparer<T>
.
namespace System.Collections
{
public sealed class Comparer : IComparer, ISerializable
{
+ public static IComparer<IEnumerable<T>> Sequence<T>(
+ IComparer<T>? elementComparer = null);
// No other static members are declared by Comparer
}
}
Draft implementation
Click to expand
private sealed class SequenceComparer<T> : IComparer<IEnumerable<T>>
{
private readonly IComparer<T> elementComparer;
public SequenceComparer(IComparer<T> elementComparer)
{
this.elementComparer = elementComparer;
}
public int Compare(IEnumerable<T>? x, IEnumerable<T>? y)
{
if (x == y) return 0;
if (x is null) return -1; // x < y
if (y is null) return 1; // x > y
using var xEnumerator = x.GetEnumerator();
using var yEnumerator = y.GetEnumerator();
while (xEnumerator.MoveNext())
{
if (!yEnumerator.MoveNext()) return 1; // x > y
var elementComparison = elementComparer.Compare(xEnumerator.Current, yEnumerator.Current);
if (elementComparison != 0) return elementComparison;
}
return yEnumerator.MoveNext()
? -1 // x < y
: 0; // x == y
}
}