2010-10-22 9 views
6

Quando ho 2 oggetti List<string>, quindi posso utilizzare direttamente Intersect e Except per ottenere un'uscita IEnumerable<string>. È abbastanza semplice, ma cosa succede se voglio l'intersezione/disgiunzione su qualcosa di più complesso?Utilizzo di LINQ per oggetti Interseca e Tranne su una proprietà specifica

Esempio, cercando di ottenere un insieme di oggetti ClassA che è il risultato della Intersect sul ClassA di AStr1 dell'oggetto e ClassB oggetto di BStr; :

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); 
    } 
} 

Come posso fissare la linea annotata per ottenere questa intersezione.

risposta

11

MoreLINQ ha ExceptBy. Non ha IntersectBy ancora, ma si potrebbe facilmente scrivere una propria implementazione, e forse anche di contribuire a MoreLINQ poi :)

Sarebbe probabilmente cercare qualcosa di di simile (omettendo il controllo degli errori):

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; 
     } 
    } 
} 

Se si desidera eseguire un'intersezione su due tipi completamente diversi che hanno avuto un tipo di proprietà comune, è possibile creare un metodo più generale con tre parametri di tipo (uno per first, uno per second e uno per la chiave comune genere).

+0

Ciao @ Jon Skeet, non capisco come posso passare il quarto parametro. Sono in grado di definire una classe implementando IEqualityComparer e quindi passare un'istanza di questa classe? – corei11

+0

@ corei11: Sì, se hai bisogno di qualcosa di personalizzato - o usa 'EqualityComparer .Default' se sei soddisfatto dell'operazione di uguaglianza predefinita. –

+0

Grazie a @Jon Skeet. Qui ho due 'IEnumerable owner1' e' owner2'. E definisco una classe chiamata 'Compar' implementando' IEqualityComparer'. Ora chiama il metodo in questo modo 'owner1 = owner1.IntersectBy (owner2, item => item.Email_Address, new Compar ())'. In realtà voglio chiarire che come passare la mia classe Compar. Quello che scrivo direttamente è una stringa o c'è un altro modo migliore o il mio approccio è ok? Il mio prototipo di classe Compar è 'public class Compar : IEqualityComparer '. Scusate. Potrebbe essere una domanda sciocca. Grazie. – corei11

3

x ∈ A ∩ B se e solo se x ∈ A e x ∈ B.

Quindi, per ogni a in aObj, è possibile controllare se a.AStr1 è nell'insieme di valori 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

questo codice:

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 superato il seguente test:

[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")); 
    } 
Problemi correlati