Why doesn't Linq provide a lambda parameter instead of IEqualityComparer <T>?

So, in things like GroupBy()

Linq, you can provide an implementation IEqualityComparer<T>

to help you compare objects. It seems, however, that it would be easier to just pass it to the lambda expression.

Example:

// current implementation
myCollection.GroupBy(c => c.Foo, c => c.Bar, new FooBarComparer());

// it seems easier to...
myCollection.GroupBy(c => c.Foo, c => c.Bar, (x, y) => x.Baz == y.Baz);

      

For a simple implementation IEqualityComparer<T>

:

public class FooBarComparer : IEqualityComparer<FooBar> {
    public bool Equals(FooBar x, FooBar y) {
        return x.Baz == y.Baz;
    }

    public int GetHashCode(FooBar obj) {
        return obj.GetHashCode();
    }
}

      

It seems like providing a lambda expression can be just as efficient. Like now, if I try to pass IEqualityComparer<T>

with a Linq query to the database, it fails because SQL Server (or whatever) doesn't know anything about my class. It seems like the lambda could be translated to SQL which can be used on the target database.

Is there a specific reason why this isn't provided as an option in Linq?

+3


source to share


2 answers


You will need two lambdas to GetHashCode

have an equivalent too. Also, it will work, yes. There are some LINQ methods that don't use hash codes, but use equality ( Enumerable.Contains

).

I assume this is just a standard API for equality that the entire BCL uses. You can easily convert between a delegate and a comparator by using a delegate-enabled comparison implementation or by converting myComparer.Equals

to a delegate.



It is not easy to drop a comparator expression to drop expressions into the database. GROUP BY

does not support this in SQL. This can certainly be made to work, but this is a niche use case (in fact, if a comparator expression for GroupBy

doesn't enforce an equality relationship, I'm not sure how that would turn out when translated to SQL).

0


source


To create an efficient GroupBy

/ Distinct

you will need two things:

  • Equality comparator
  • Hash generator for creating a hash dictionary

OR you can follow the C ++ route

  • A comparator capable of ordering elements so that you can create a tree

If you only have an equality matcher, then the difficulty of doing GroupBy

is something like O (n ^ 2), because if you have 5 elements you need 5 + 4 + 3 + 2 + 1 comparisons, so n * ( n + 1) / 2 so 15. This is what a "good" library will never let you do (and no sane SQL server will ever!)

Now obviously the LINQ library could parse your equality lambda, see that it is

(x, y) => x.Baz == y.Baz

      

we see that it is symmetric, so that the left term and the right term have the form



x => x.Baz

      

and use that to generate hash and select comparator. But for now, it would be easier to do directly

myCollection.GroupBy(c => c.Foo.Baz) 

      

Yes, what can you do :-)

And then,

myCollection.GroupBy(c => c.Foo.Baz, c => new { c.Foo, c.Bar })
            .Select(c => new { Key = c.First().Foo, Values = c.Select(x => x.Bar) })

      

This is very similar to your intended GroupBy

(the only difference is that the values ​​are in Values

IEnumerable<>

)

Now ... to use overloads with IEqualityComparer<T>

... as I wrote in the comments, I think they should be used with " overloads " that the LINQ provider can recognize as being different StringComparer.*

(for example StringComparer.OrdinalIgnoreCase

) and EqualityComparer<T>.Default

that is the default mapping.

0


source







All Articles