LINQ Seleccione Distinto con Tipos Anónimos


Así que tengo una colección de objetos. El tipo exacto no es importante. De ella quiero extraer todos los pares únicos de un par de propiedades particulares, así:

myObjectCollection.Select(item=>new
                                {
                                     Alpha = item.propOne,
                                     Bravo = item.propTwo
                                }
                 ).Distinct();

Así que mi pregunta es: ¿Distinct en este caso usará el objeto predeterminado equals (que será inútil para mí, ya que cada objeto es nuevo) o se le puede decir que haga un igual diferente (en este caso, valores iguales de Alfa y Bravo => instancias iguales)? ¿Hay alguna manera de lograr ese resultado, si esto no lo hace?

Author: GWLlosa, 2009-02-13

8 answers

Tener una lectura a través de K. Scott Allen excelente post aquí:

E Igualdad para Todos ... Tipos anónimos

La respuesta corta (y cito):

Resulta que el compilador de C# anula Equals y GetHashCode para anónimo tipo. La aplicación de los dos métodos anulados utiliza todo el público propiedades del tipo para calcular un código hash del objeto y prueba para igualdad. Si dos objetos de la misma el tipo anónimo tiene todo lo mismo valores para sus propiedades-el los objetos son iguales.

Así que es totalmente seguro usar el método Distinct() en una consulta que devuelve tipos anónimos.

 170
Author: Matt Hamilton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-12 21:59:11
public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();

Lo siento por el formato desordenado anterior

 14
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-27 00:45:41

Interesante que funciona en C# pero no en VB

Devuelve las 26 letras:

var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
MyBet.ToCharArray()
.Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()})
.Distinct()
.Dump();

Devuelve 52...

Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
MyBet.ToCharArray() _
.Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _
.Distinct() _
.Dump()
 4
Author: GeorgeBarker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-26 20:58:11

Hice una pequeña prueba y encontré que si las propiedades son tipos de valor, parece funcionar bien. Si no son tipos de valor, entonces el tipo necesita proporcionar sus propias implementaciones Equals y GetHashCode para que funcione. Las cuerdas, creo, funcionarían.

 3
Author: tvanfosson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-12 22:07:45

Puede crear su propio método de extensión Distinto que toma la expresión lambda. Este es un ejemplo

Crear una clase que se deriva de IEqualityComparer interfaz

public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

Luego crea tu método de extensión Distinto

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

Y puede usar este método para encontrar elementos distintos

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();
 2
Author: Buildstarted,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-11 22:31:16

Si Alpha y Bravo ambos heredan de una clase común, podrá dictar la comprobación de igualdad en la clase padre implementando IEquatable<T>.

Por ejemplo:

public class CommonClass : IEquatable<CommonClass>
{
    // needed for Distinct()
    public override int GetHashCode() 
    {
        return base.GetHashCode();
    }

    public bool Equals(CommonClass other)
    {
        if (other == null) return false;
        return [equality test];
    }
}
 0
Author: ern,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-12 22:00:16

Hey ahí tengo el mismo problema y encontré una solución. Usted tiene que implementar la interfaz IEquatable o simplemente anular el (Es Igual a los Métodos & GetHashCode). Pero este no es el truco, el truco que viene en el método GetHashCode. Usted no debe devolver el código hash del objeto de su clase, sino que debe devolver el hash de la propiedad que desea comparar así.

public override bool Equals(object obj)
    {
        Person p = obj as Person;
        if ( obj == null )
            return false;
        if ( object.ReferenceEquals( p , this ) )
            return true;
        if ( p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian )
            return true;
        return false;
        //return base.Equals( obj );
    }
    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }

Como ves tengo una clase llamada persona tiene 3 propiedades (Nombre, Edad, IsEgyptian "Porque soy") En el GetHashCode devolví el hash de la propiedad Name, no el objeto Person.

Pruébalo y funcionará ISA. Agradecer, Modather Sadik

 0
Author: Modather Sadik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-16 14:51:48

Para que funcione en VB.NET, necesita especificar la palabra clave Key antes de cada propiedad en el tipo anónimo, así:

myObjectCollection.Select(Function(item) New With
{
    Key .Alpha = item.propOne,
    Key .Bravo = item.propTwo
}).Distinct()

Estaba luchando con esto, pensé VB.NET no es compatible con este tipo de característica, pero en realidad lo hace.

 0
Author: Alisson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-13 20:24:30