Bushuev
Bushuev

Reputation: 577

How to write IEqualityComparer for anonymous variables in lambda expression?

I want to know there is a way to implement IEqualityComparer for anonymous variables in a lambda expression or, in any case, I need to write class to transform anonymous variables to the sprecific class and create a class where I need to implement IEqualtyComparer ?

I write code which create Cartesian(Decart's) production: I defined class Decart.

public class Decart
{
    public int X;
    public int Y;
}

I defined IEqualtityComparer for Decart class

public class Ext : IEqualityComparer<Decart>
{

    public bool Equals(Decart x, Decart y)
    {
        if ((x.X == y.X && x.Y == y.Y) ||
            x.X == y.Y && x.Y == y.X)
            return true;

        return false;
    }

    public int GetHashCode(Decart obj)
    {
        return obj.X + obj.Y;
    }
}

I run this code:

static void Main(string[] args)
{
    Ext ext = new Ext();
    IEnumerable<int> input = Enumerable.Range(1, 3);
        var secondResult = input
        .SelectMany(x => input.Select(y => new Decart{ X = x, Y = y }))
        .Distinct(new Ext());
    Console.WriteLine(new string('-', 50));

    foreach (var x in secondResult)
    {
        Console.WriteLine(string.Format("{0} {1}", x.X, x.Y));
    }
    //output:
    //1 1
    //1 2
    //1 3
    //2 2
    //2 3
    //3 3
}

I want to run next code without defining a class for anonymous variables, a class for implementing IEqualityComparer.

    var thirdResult = input
        .SelectMany(x => input
            .SelectMany(y => input
                .Select(z => new { x, y, z })))
                    .Distinct( ???? );

//in this case output need to be like this:
//1 1 1
//1 2 1
//1 3 1
//2 1 2
//2 2 2
//2 3 2
//3 1 3
//3 2 3
//3 3 3

How to do it ?

Upvotes: 4

Views: 5176

Answers (2)

user4003407
user4003407

Reputation: 22132

You can declare IEqualityComparer<T> implementation which would take delegates as implementation for GetHashCode and Equals methods of interface. Then you can pass-in anonymous methods as implementation.

public static IEqualityComparer<T> CreateEqualityComparer<T>(T ignore, Func<T, int> getHashCode, Func<T, T, bool> equals) => new DelegatedEqualityComparer<T>(getHashCode, equals);

public class DelegatedEqualityComparer<T> : EqualityComparer<T> {
    private Func<T, int> getHashCode;
    private Func<T, T, bool> equals;
    public DelegatedEqualityComparer(Func<T, int> getHashCode, Func<T, T, bool> equals) {
        if(getHashCode==null) throw new ArgumentNullException(nameof(getHashCode));
        if(equals==null) throw new ArgumentNullException(nameof(equals));
        this.getHashCode=getHashCode;
        this.equals=equals;
    }
    public override int GetHashCode(T x) => getHashCode(x);
    public override bool Equals(T x, T y) => equals(x, y);
}

And you use it like this:

var equalityComparer = CreateEqualityComparer(true ? null : new { x = 0, y = 0 }, a => a.x+a.y, (a, b) => (a.x==b.x&&a.y==b.y)||(a.x==b.y&&a.y==b.x));
var result = input
    .SelectMany(x => input
        .Select(y => new { x, y }))
    .Distinct(equalityComparer);

Meaning of true ? null : new { x = 0, y = 0 }:

First argument to CreateEqualityComparer (T ignore) is needed to allow compiler to infer type T, as you can not spell name of anonymous type. true condition of ternary operator make compiler to always select left branch null, but as both branches of ternary operator must return the same type, then new { x = 0, y = 0 } make compiler to implicitly cast null to given anonymous type.

Also, relevant note from specification:

7.6.10.6 Anonymous object creation expressions
Within the same program, two anonymous object initializers that specify a sequence of properties of the same names and compile-time types in the same order will produce instances of the same anonymous type.

Upvotes: 7

user6996876
user6996876

Reputation:

It's unclear what you're asking.

I assume you want to generilize the dimensions of this class like

public class Decart
{
    public int[] dim;
}

then you can extend the comparer

public class Ext : IEqualityComparer<Decart>
{

    public bool Equals(Decart x, Decart y)
    {
        for(int i=0; i< x.dim.Length;i++) 
        if (x.dim[i] != y.dim[i] ) 
            return false; 
        return true;
    }

    public int GetHashCode(Decart obj)
    {
        return obj.dim.Sum();
    }
}

Upvotes: 2

Related Questions