2012-05-29 8 views
9

Gibt es eine Möglichkeit, mehrere Schlüssel für die von LINQ bereitgestellte .ToLookup-Funktion zu benötigen?ToLookup mit mehreren Schlüsseln

Ich gebe zu, dass dies auf den ersten Blick nicht intuitiv scheint, und ich erwarte, dass es keine Möglichkeit gibt, dies zu tun, aber ich hoffe, dass jemand einen Weg kennt.

Ich möchte grundsätzlich in der Lage sein, nach zwei Werten zu suchen, zum Beispiel einer string und einer int, und das Objekt mit diesen beiden Werten abrufen.

Beispiel

public class MyClass { 
     public string StringProp {get;set;} 
     public int IntProp {get;set;} 
     public object MoreData {get;set;} 
    } 

    public class Main { 
     public void Main() { 
     HashSet<MyClass> set = new HashSet<MyClass>(); 
     set.Add(new MyClass {StringProp = "a", IntProp = 1, MoreData = null}); 
     set.Add(new MyClass {StringProp = "c", IntProp = 4, MoreData = new object()}); 
     set.Add(new MyClass {StringProp = "a", IntProp = 2, MoreData = "upupdowndown"}); 
     set.Add(new MyClass {StringProp = "c", IntProp = 1, MoreData = string.Empty}); 
     set.Add(new MyClass {StringProp = "c", IntProp = 4, MoreData = string.Empty}); 
     // Using 'var' because I don't know how this would be defined. 
     // I recognize that this will not compile - but this is what I'm trying to do. 
     var lookup = set.ToLookup(x => x.StringProp && x.IntProp) 
     MyClass c = lookup["a", 1].First(); // Should return the first element 
     IEnumerable<MyClass> list = lookup["c", 4]; // Should return the 2nd and last elements 
     } 
    } 

Antwort

17

I Tuple s für diese Art der Sache verwenden würde:

var lookup = set.ToLookup(x => Tuple.Create(x.StringProp, x.IntProp)); 
MyClass c = lookup[Tuple.Create("a", 1)].First(); 
IEnumerable<MyClass> list = lookup[Tuple.Create("c", 4)]; 
+0

ich nicht sehen, wie das hilft. – DarthVader

+0

Danke Gabe. Ich werde das ein wenig ausprobieren und Sie wissen lassen, wie es funktioniert. – Merwer

4

Obwohl nicht das, was Sie wirklich wollen, aber den Job ganz gut tun wird:

var r = set.ToLookup(x => new { x.StringProp, x.IntProp }); 
var f = r[ new { StringProp = "a", IntProp = 1 } ].First(); 
7

Also die anderen Antworten sind in der Richtung, die ich dachte, aber es ist etwas umständlich zu sein Erstellen Sie jedes Mal ein Tupel oder eine anonyme Klasse, wenn Sie nur einen Wert aus der Suche herausholen möchten. Wäre es nicht großartig, wenn Sie einfach einen String und einen Int in einen Indexer stecken könnten, um den Wert zu erhalten. Indem Sie Ihre eigene Klasse erstellen, um die Suche mit einem Indexer zu umhüllen, können Sie genau das tun. Hier

public class MyLookup<T1, T2, TOut> 
{ 
    private ILookup<Tuple<T1, T2>, TOut> lookup; 
    public MyLookup(IEnumerable<TOut> source, Func<TOut, Tuple<T1, T2>> keySelector) 
    { 
     lookup = source.ToLookup(keySelector); 
    } 

    public IEnumerable<TOut> this[T1 first, T2 second] 
    { 
     get 
     { 
      return lookup[Tuple.Create(first, second)]; 
     } 
    } 

    //feel free to either expose the lookup directly, or add other methods to access the lookup 
} 

ist ein Beispiel dafür verwendet werden:

IEnumerable<MyClass> data = null; //TODO populate with real data 
var lookup = new MyLookup<string, int, MyClass>(data 
    , item => Tuple.Create(item.StringProp, item.IntProp)); 

IEnumerable<MyClass> someValue = lookup["c", 4]; 
+0

Das ist eine wirklich gute Idee, aber ich habe Gabes Antwort als richtig markiert, da ich bereits genug Abstraktion gemacht habe, um den Code hinter der täglichen Entwicklung zu verstecken. – Merwer

+0

Das ist etwas umständlich, aber das würde ich umsetzen, wenn ich so etwas häufig machen müsste. – Gabe

+0

@Gabe Die 'MyLookup' Klasse ist bereits implementiert, so dass jede Instanz davon nicht mehr zu erstellen braucht als Ihre Lösung, und die Verwendung der resultierenden" Suche "ist wesentlich einfacher. Ob es sich gelohnt hat, "MyLookup" zu schreiben, ist eine gute Frage, aber angesichts der Tatsache, dass es schon erledigt ist, ist die Frage, ob man es benutzen soll, viel einfacher zu beantworten. – Servy