2010-10-22 13 views
6

Cuando tengo 2 objetos List<string>, entonces puedo usar Intersect y Except en ellos directamente para obtener una salida IEnumerable<string>. Eso es bastante simple, pero ¿y si quiero que la intersección/disyunción en algo más complejo?Uso de LINQ para objetos Intersecar y Excepto en una propiedad específica

ejemplo, tratando de obtener una colección de objetos ClassA que es el resultado de la intersecan en ClassA de AStr1 objeto y el objeto de ClassBBStr; :

public class ClassA { 
    public string AStr1 { get; set; } 
    public string AStr2 { get; set; } 
    public int AInt { get; set; } 
} 
public class ClassB { 
    public string BStr { get; set; } 
    public int BInt { get; set; } 
} 
public class Whatever { 
    public void xyz(List<ClassA> aObj, List<ClassB> bObj) { 
     // *** this line is horribly incorrect *** 
     IEnumberable<ClassA> result = 
      aObj.Intersect(bObj).Where(a, b => a.AStr1 == b.BStr); 
    } 
} 

Cómo puedo reparar la línea indicada para lograr esta intersección.

Respuesta

11

MoreLINQ tiene ExceptBy. No tiene IntersectBy todavía, pero usted podría fácilmente escribir su propia aplicación, y posiblemente incluso contribuir a MoreLINQ después :)

que probablemente se vería algo como esto (la omisión de la comprobación de errores):

public static IEnumerable<TSource> IntersectBy<TSource, TKey>(
    this IEnumerable<TSource> first, 
    IEnumerable<TSource> second, 
    Func<TSource, TKey> keySelector, 
    IEqualityComparer<TKey> keyComparer) 
{ 
    HashSet<TKey> keys = new HashSet<TKey>(first.Select(keySelector), 
              keyComparer); 
    foreach (var element in second) 
    { 
     TKey key = keySelector(element); 
     // Remove the key so we only yield once 
     if (keys.Remove(key)) 
     { 
      yield return element; 
     } 
    } 
} 

Si desea realizar una intersección en dos tipos completamente diferentes que tienen un tipo de propiedad común, puede hacer un método más general con tres parámetros de tipo (uno para first, uno para second y uno para la clave común tipo).

+0

Hola @ Jon Skeet, no entiendo cómo puedo pasar el cuarto parámetro. ¿Defino una clase implementando IEqualityComparer y luego paso una instancia de esta clase? – corei11

+0

@ corei11: Sí, si necesita algo personalizado, o use 'EqualityComparer .Default' si está contento con la operación de igualdad predeterminada. –

+0

Gracias @Jon Skeet. Aquí tengo dos 'IEnumerable owner1' y' owner2'. Y defino una clase llamada 'Compar' implementando' IEqualityComparer'. Ahora llame al método de esta manera 'owner1 = owner1.IntersectBy (owner2, item => item.Email_Address, new Compar ())'. En realidad, quiero aclarar cómo pasar mi clase Compar. ¿Eso es directamente yo escribo una cuerda o hay otra manera mejor o mi enfoque está bien? El prototipo de mi clase Compar es 'clase pública Compar : IEqualityComparer '. Lo siento. Puede ser una pregunta tonta. Gracias. – corei11

3

x ∈ A ∩ B si y sólo si x ∈ A y B. x ∈

Así, para cada a en aObj, se puede comprobar si es a.AStr1 en el conjunto de valores BStr.

public void xyz(List<ClassA> aObj, List<ClassB> bObj) 
{ 
    HashSet<string> bstr = new HashSet<string>(bObj.Select(b => b.BStr)); 
    IEnumerable<ClassA> result = aObj.Where(a => bstr.Contains(a.AStr1)); 
} 
1

este código:

public IEnumerable<ClassA> xyz(List<ClassA> aObj, List<ClassB> bObj) 
    { 
     IEnumerable<string> bStrs = bObj.Select(b => b.BStr).Distinct(); 
     return aObj.Join(bStrs, a => a.AStr1, b => b, (a, b) => a); 
    } 

ha pasado la prueba siguiente:

[TestMethod] 
    public void PropertyIntersectionBasedJoin() 
    { 
     List<ClassA> aObj = new List<ClassA>() 
           { 
            new ClassA() { AStr1 = "a" }, 
            new ClassA() { AStr1 = "b" }, 
            new ClassA() { AStr1 = "c" } 
           }; 
     List<ClassB> bObj = new List<ClassB>() 
           { 
            new ClassB() { BStr = "b" }, 
            new ClassB() { BStr = "b" }, 
            new ClassB() { BStr = "c" }, 
            new ClassB() { BStr = "d" } 
           }; 

     var result = xyz(aObj, bObj); 

     Assert.AreEqual(2, result.Count()); 
     Assert.IsFalse(result.Any(a => a.AStr1 == "a")); 
     Assert.IsTrue(result.Any(a => a.AStr1 == "b")); 
     Assert.IsTrue(result.Any(a => a.AStr1 == "c")); 
    } 
Cuestiones relacionadas