two lists c# linq intersection intersect

c# - lists - Intersecta con un IEqualityComparer personalizado usando Linq



intersect c# list (2)

Para resumir: tengo 2 colecciones de objetos. Uno contiene buenos valores (llamémoslo "Bueno"), los otros valores predeterminados (Sr. "Predeterminado"). Quiero el Intersecto de la Unión entre Bueno y Predeterminado, y Predeterminado. En otras palabras: Intersecar (Unión (Bueno, Predeterminado), Predeterminado). Uno podría pensar que se resuelve como Predeterminado, pero aquí es donde se vuelve complicado: yo uso un IEqualityComparer personalizado.

Obtuve las siguientes clases:

class MyClass { public string MyString1; public string MyString2; public string MyString3; } class MyEqualityComparer : IEqualityComparer<MyClass> { public bool Equals(MyClass item1, MyClass item2) { if(item1 == null && item2 == null) return true; else if((item1 != null && item2 == null) || (item1 == null && item2 != null)) return false; return item1.MyString1.Equals(item2.MyString1) && item1.MyString2.Equals(item2.MyString2); } public int GetHashCode(MyClass item) { return new { item.MyString1, item.MyString2 }.GetHashCode(); } }

Aquí están las características de mis colecciones Good and Default collections:

Valor predeterminado: es un conjunto grande que contiene todos los pares {MyString1, MyString2} deseados, pero los valores de MyString3 son, como puede adivinar, valores predeterminados.

Bueno: es un conjunto más pequeño, que contiene principalmente elementos que están en el conjunto Predeterminado, pero con algunos buenos valores de MyString3. También tiene algunos {MyString1, MyString2} que están fuera del conjunto deseado.

Lo que quiero hacer es esto: tomar solo los artículos de Bueno que están en Predeterminado, pero agregue los otros ítems en Predeterminado a eso.

Aquí está, lo que creo que es mi mejor intento:

HalfWantedResult = Good.Union(Default, new MyEqualityComparer()); WantedResult= HalfWantedResult.Intersect(Good, new MyEqualityComparer());

Enseñé que debería haber funcionado, pero el resultado que obtengo es básicamente el conjunto de buenos pares {MyString1, MyString2}, pero todos provienen del conjunto Predeterminado, por lo que tengo el valor predeterminado en todos lados. También intenté cambiar los valores predeterminados y buenos del último Intersect, pero obtengo el mismo resultado.


En primer lugar, esto es incorrecto:

public bool Equals(MyClass item1, MyClass item2) { return GetHashCode(item1) == GetHashCode(item2); }

Si los códigos hash son diferentes, con seguridad los 2 elementos correspondientes son diferentes, pero si son iguales no se garantiza que los 2 elementos correspondientes sean iguales.

Esta es la implementación correcta de Equals :

public bool Equals(MyClass item1, MyClass item2) { if(object.ReferenceEquals(item1, item2)) return true; if(item1 == null || item2 == null) return false; return item1.MyString1.Equals(item2.MyString1) && item1.MyString2.Equals(item2.MyString2); }

Como sugirió Slacks (anticipándome), el código es el siguiente:

var Default = new List<MyClass> { new MyClass{MyString1="A",MyString2="A",MyString3="-"}, new MyClass{MyString1="B",MyString2="B",MyString3="-"}, new MyClass{MyString1="X",MyString2="X",MyString3="-"}, new MyClass{MyString1="Y",MyString2="Y",MyString3="-"}, new MyClass{MyString1="Z",MyString2="Z",MyString3="-"}, }; var Good = new List<MyClass> { new MyClass{MyString1="A",MyString2="A",MyString3="+"}, new MyClass{MyString1="B",MyString2="B",MyString3="+"}, new MyClass{MyString1="C",MyString2="C",MyString3="+"}, new MyClass{MyString1="D",MyString2="D",MyString3="+"}, new MyClass{MyString1="E",MyString2="E",MyString3="+"}, }; var wantedResult = Good.Intersect(Default, new MyEqualityComparer()) .Union(Default, new MyEqualityComparer()); // wantedResult: // A A + // B B + // X X - // Y Y - // Z Z -


Necesita verificar la igualdad real, no solo la igualdad de hashcode.

GetHashCode() no está (y no puede estar) libre de colisiones, por lo que se requiere el método Equals en primer lugar.

Además, puedes hacer esto mucho más simplemente escribiendo

WantedResult = Good.Concat(Default).Distinct();

El método Distinct devolverá el primer elemento de cada par de duplicados, por lo que devolverá el resultado deseado.

EDITAR : Eso debería ser

WantedResult = Good.Intersect(Default, new MyEqualityComparer()) .Union(Default, new MyEqualityComparer());