2013-02-09 9 views
5

Ich habe zwei Instanzen des Typs IEnumerable wie folgt.Ermitteln eines Schnittpunkts von zwei IEnumerables mit LINQ

IEnumerable<Type1> type1 = ...; 
IEnumerable<Type2> type2 = ...; 

Sowohl Type1 und Type2 enthalten Mitglied common genannt, so dass, obwohl sie von anderen Klasse sind, können wir immer noch sie so beziehen.

type1[0].common == type2[4].common 

Ich versuche, jene Elemente der type1, die keinen entsprechenden common Wert in type2 müssen Filter entfernt und ein Wörterbuch auf einem Wert von jeweils bezogen erstellen. Im Moment mache ich das mit der folgenden Doppelschleife.

Dictionary<String, String> intersection = ...; 
foreach (Type1 t1 in type1) 
    foreach(Type2 t2 in type2) 
    if (t1.common == t2.common) 
     intersection.Add(t1.Id, t2.Value); 

Jetzt habe ich mit LINQ versucht, aber alle .Where, .Select und .ForEach gab mir nur Kopfschmerzen. Gibt es eine Möglichkeit, die gleiche Operation mit LINQ sauber durchzuführen?

+0

LINQ wird sowieso nur aufzählen. Haben Sie Leistungsprobleme? – Paparazzi

+0

Nein. Aber die anderen Programmierer lachen über mich. :(Sie sagen, ich bin zu müde, um den Code zu finden, und das ist nicht möglich. Während ich das erste zugeben kann, weigere ich mich, dem letzteren zuzustimmen. :) –

+1

Also was passiert, wenn Sie einen t1 haben, der * zwei * t2s hat dieses Spiel? –

Antwort

14

Wenn zwei Sequenzen etwas gemeinsam haben, und Sie möchten ihr Produkt filtern, auf der Grundlage dieser Gemeinsamkeit, die effiziente Abfrage ist eine Verknüpfung. Angenommen Typ1 ist Kunde und Typ2 ist Bestellen. Jeder Kunde hat eine CustomerID, und jede Bestellung hat auch eine CustomerID. Dann kannst du das sagen.

var query = from customer in customers 
      join order in orders 
       on customer.CustomerId equals order.CustomerId 
      select new { customer.Name, order.Product }; 

Iterieren, dass Sie eine Folge von Paaren geben wird von jedem Kundennamen aus, die einen Auftrag hat, und alle ihre Produkte. Also, wenn Kunde Suzy einen Pfannkuchen und eine Pizza bestellte und Kunde Bob ein Steak bestellte, würden Sie diese Paare bekommen.

Suzy, pancake 
Suzy, pizza 
Bob, steak 

Wenn Sie diese Gruppen so gruppieren möchten, dass jeder Kunde eine Liste seiner Aufträge hat, wird eine Gruppe hinzugefügt.

Iteration, die Ihnen Paare gibt, wobei der erste Artikel der Name und der zweite Artikel eine Sequenz von Produkten ist.

Suzy, { pancake, pizza } 
Bob, { steak } 
+0

Reines Gold, hihi! Für diese Zeit war der erste Ansatz genau das, was ich brauchte (ich weiß nicht, wie ich es selbst einrichten konnte). Aber der andere ist neu für mich. Ich wusste nichts von der Gruppierung, aber es wird mir bald recht nützlich sein. –

+0

Ich denke, die äquivalente Extension-Methode Aufrufe zusätzlich der Abfrage-Ausdruck wäre es einfacher zu verstehen, was passiert, und was die Leistungsmerkmale sind. – CodesInChaos

-1
type1.where(i=>type2.where(j=>j.common == i.common).Count > 0); 

Dies sollte Ihnen eine Liste von nur diejenigen, die übereinstimmen.

+0

Dies ist ein bisschen weniger effizient als ein 'Join'. Sie machen hier eine Vielzahl von Iterationen von 'type2' durch lineare Suchen, im Gegensatz zu satzbasierten Operationen auf Mengen. Sie geben auch nicht den 't1'-Eintrag mit den Verknüpfungen in' type2' aus. – Servy

1

Eine andere Option wäre Beitritt. Ich habe unten eine schnelle Konsolen-App gemacht, musste aber meine eigenen Daten zusammenstellen. Hoffe ich habe deine Frage richtig verstanden.

public class Type1 
{ 
    public string ID { get; set; } 
    public Guid common { get; set; } 
} 
public class Type2 
{ 
    public string Value { get; set; } 
    public Guid common { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Guid CommonGuid = Guid.NewGuid(); 

     IEnumerable<Type1> EnumType1 = new List<Type1>() 
     { 
      new Type1() { 
       ID = "first", 
       common = CommonGuid 
      }, 
      new Type1() { 
       ID = "second", 
       common = CommonGuid 
      }, 
      new Type1() { 
       ID = "third", 
       common = Guid.NewGuid() 
      } 
     } as IEnumerable<Type1>; 

     IEnumerable<Type2> EnumType2 = new List<Type2>() 
     { 
      new Type2() { 
       Value = "value1", 
       common = CommonGuid 
      }, 
      new Type2() { 
       Value = "value2", 
       common = Guid.NewGuid() 
      }, 
      new Type2() { 
       Value = "value3", 
       common = CommonGuid 
      } 
     } as IEnumerable<Type2>; 

     //--The part that matters 
     EnumType1      //--First IEnumerable 
      .Join(      //--Command 
       EnumType2,    //--Second IEnumerable 
       outer => outer.common, //--Key to join by from EnumType1 
       inner => inner.common, //--Key to join by from EnumType2 
       (inner, outer) => new { ID = inner.ID, Value = outer.Value }) //--What to do with matching "rows" 
      .ToList() //--Not necessary, just used so that I can use the foreach below 
      .ForEach(item => 
       { 
        Console.WriteLine("{0}: {1}", item.ID, item.Value); 
       }); 

     Console.ReadKey(); 
    } 
} 

angezeigt unten:
zuerst: value1
zuerst: value3
Sekunde: value1
Sekunde: value3

+0

Yupp. Gutes Beispiel - sehr klar und informativ. Es gelang mir, die Operation auf nur vier Zeilen LINQ zu verkürzen. –

0

Unter der Annahme, dass Sie immer noch die Kreuzung als ein Dictionary<string, string> behalten möchten:

IEnumerable<Type1> list1; 
IEnumerable<Type2> list2; 

Dictionary<string, string> intersection = 
    (from item1 in list1 
    from item2 in list2 
    where item1.common = item2.common 
    select new { Key = item1.Id, Value = item2.Value }) 
     .ToDictionary(x => x.Key, x => x.Value); 
+0

Dies ist ein wenig weniger effizient als ein 'Join'. Sie erstellen viele Paare und filtern sie dann, im Gegensatz zu einem Join, der von Anfang an nur die richtigen Paare erstellt. – Servy

-1

Ich vermisse etwas, aber woul d tun:

type1 
.where(t1 => type2.Any(t2 => t1.common == t2.common) 
.ToDictionary(t1 => t1.Id) 

Oder wie Servy

vorgeschlagen
type1 
    .Join(type2, a => a.common, b => b.common, (a1,b1) => a1) 
    .ToDictionary(t1 => t1.Id) 
+0

Dies ist ein wenig weniger effizient als ein 'Join'. Sie machen hier eine Vielzahl von Iterationen von 'type2' durch lineare Suchen, im Gegensatz zu satzbasierten Operationen auf Mengen. Sie geben auch nicht den 't1'-Eintrag mit den Verknüpfungen in' type2' aus. – Servy

+0

@Servy Sie sind richtig. Effizienz war nie ein Anliegen. Ich habe versucht, LINQ-Konzepte jemandem zu vermitteln, der mit for-Schleifen besser vertraut ist. Warum postest du deine Antwort nicht! –

+0

Warum sollte ich eine Antwort posten, wo es bereits zwei Antworten gibt, die dieses Problem * richtig * lösen, besonders wenn man von Eric Lippert kommt. Die Vermittlung von LINQ-Konzepten bedeutet meiner Meinung nach, die geeignete (n) Methode (n) für die jeweilige Aufgabe zu lehren, was nicht der Fall ist. Als allgemeine Regel gilt, dass jedes Mal, wenn Sie das Muster von 'Where' mit einem verschachtelten' Any' sehen, etwas nicht stimmt. 'type2' sollte ein' HashSet' sein, das 'Contains' verwendet, wenn das überhaupt möglich ist. In jedem Fall funktioniert diese Lösung * nicht einmal *. Das Ergebnis muss das * Paar * von Elementen sein, nicht nur eines von ihnen. – Servy

Verwandte Themen