2013-06-26 13 views
28

Dire che ho il seguente:confrontare due liste Via Uno proprietà utilizzando LINQ

class Widget1{ 
     public int TypeID { get; set; } 
     public string Color { get; set; } 
    } 

    class Widget2 
    { 
     public int TypeID { get; set; } 
     public string Brand { get; set; } 
    } 

    private void test() 
    { 
     List<Widget1> widgets1 = new List<Widget1>(); 
     List<Widget2> widgets2 = new List<Widget2>(); 
     List<Widget1> widgets1_in_widgets2 = new List<Widget1>(); 

     //some code here to populate widgets1 and widgets2 

     foreach (Widget1 w1 in widgets1) 
     { 
      foreach (Widget2 w2 in widgets2) 
      { 
       if (w1.TypeID == w2.TypeID) 
       { 
        widgets1_in_widgets2.Add(w1); 
       } 
      } 
     } 
    } 

Sto usando due foreach loop per confrontare le liste da TypeID per popolare una terza lista. C'è un altro modo con LINQ per confrontare questi due elenchi tramite il TypeID? Forse usando Interstect o qualche altra funzione?

risposta

33

Quello che vuoi qui è un Join.

var widgets1_in_widgets2 = from first in widgest1 
    join second in widgets2 
    on first.TypeID equals second.TypeID 
    select first; 

Intersect può essere più o meno pensato come un caso speciale di Join dove le due sequenze sono dello stesso tipo, e può quindi essere applicato per la parità invece di aver bisogno di una proiezione per ciascun tipo per generare una chiave per confrontare. Dato il tuo caso, Intersect non è un'opzione.

Se un particolare ID è duplicato nel secondo set e non si vuole l'oggetto da duplicare nei risultati, allora è possibile utilizzare un GroupJoin invece di un Join:

var widgets1_in_widgets2 = from first in widgest1 
    join second in widgets2 
    on first.TypeID equals second.TypeID 
    into matches 
    where matches.Any() 
    select first; 
+0

+1 _ "Dato il vostro caso, Intersect non è un'opzione" _ Sarebbe un'opzione quando una consuetudine '' IEqualityComparer sarebbe possibile, quindi un tipo di base comune. Quindi potresti passare a [questo overload] (http://msdn.microsoft.com/en-us/library/bb355408.aspx) o sovrascrivere 'Equals' +' GetHashCode' in 'BaseWidget' per confrontare con' TypeID 'di default. –

3

adesione è l'inconveniente che i tuoi risultati potrebbero essere duplicati se widgets1 o widgets2 contengono elementi con lo stesso TypeID più di uno (che si applica anche al tuo codice originale, tra l'altro).

Quanto segue farà esattamente ciò che si desidera: restituisce tutti gli elementi dai widget1 per i quali esiste un elemento con un TypeID corrispondente nei widget2.

widgets1_in_widgets2 = (from w1 in widgets1 
         where widgets2.Any(w2 => w1.TypeID == w2.TypeID) 
         select w1).ToList() 
+1

Mentre questo genererà l'output corretto, avrà delle prestazioni piuttosto scadenti mentre stai facendo una ricerca lineare su una collezione per ogni oggetto in un'altra collezione. – Servy

+0

'" Join ha lo svantaggio che i tuoi risultati potrebbero essere duplicati se widgets1 o widgets2 contengono elementi con lo stesso TypeID più di uno. "Il codice nell'OP avrebbe in realtà lo stesso * esatto * output di un" Join ", quindi o non ha chiavi duplicate in nessuna lista, o * vuole * i risultati duplicati. – Servy

+0

@Servy: buon punto, lo menzionerò nella mia risposta. Tuttavia, la denominazione delle variabili mi fa presumere che ciò non sia intenzionale. – Heinzi

28

È possibile farlo

widgets2.Where(y=>widget1.Any(z=>z.TypeID==y.TypeID)); 
+4

Mentre questo genera l'output corretto, avrà delle prestazioni piuttosto scadenti mentre stai facendo una ricerca lineare su una collezione per ogni oggetto in un'altra collezione. – Servy

+0

@Servy hmmm..indeed – Anirudha

+1

La modifica più semplice consiste semplicemente nel memorizzare i TypeID di uno dei set in un 'HashSet' che può essere cercato molto più rapidamente. Un cambiamento più significativo sarebbe semplicemente usare 'Join', come ho mostrato, anche se finirà per fare praticamente la stessa cosa sotto il cofano. – Servy

2

Prova con un sovraccarico di "Dove"

var isMatch = !widgets1.Where((w1, index) => w1.TypeId == widgets2[index].TypeId)).Any(); 
+1

Una risposta con un solo codice e nessuna spiegazione, soprattutto quando la questione è stata accuratamente risposto prima – ItamarG3

+0

Si potrebbe aggiornare questa risposta per specificare come è diverso. Trovo che questa sia una soluzione proposta utile nel caso in cui si desidera confrontare l'ordine ed evitare qualsiasi ricerca lineare. – DannyMeister

0

Mi piace questa soluzione perché è semplice da leggere nel codice.

bool result = firstList.All(o => secondList.Any(w => w.Prop1 == o.Prop1 && w.Prop2 == o.Prop2)); 

Vedi l'esempio completo in violino: Fiddle example comparation

+0

Thx, questo ha funzionato per me. if (projectIds.All (y => r.ProjectsId.Any (z => z.Equals (y)))) { reports.Add (r); } – Parveen

Problemi correlati