Skip to content

API proposal: Sequence comparer #33873

Open
@jnm2

Description

@jnm2

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
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions