2012-05-29 157 views
9

LINQ提供的.ToLookup函數是否需要多個鍵?ToLookup with multiple keys

我承認這起初看起來不直觀,我期待沒有實際的方法來做到這一點,但我希望有人知道一種方式。

我基本上希望能夠通過兩個值查找,例如stringint,並檢索具有這兩個值的對象。

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

回答

17

我會用Tuple S表示這樣的事情:

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

我沒有看到這是如何幫助。 – DarthVader

+0

謝謝Gabe。我將嘗試一點,並讓你知道它是如何工作的。 – Merwer

4

雖然不是你真正想要的,但會做的工作就好了:

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

所以其他答案是我所想的,但是有點麻煩每次你只想從查找中獲得一個值時創建一個元組或匿名類。如果你可以將一個字符串和一個int值粘貼到一個索引器中來獲取值,那會不會很好。通過創建你自己的類來包裝索引器的查找,你可以做到這一點。

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 
} 

這裏是它使用的例子:

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

這真是一個好主意,但我標記Gabe的答案是正確的,因爲我已經做了足夠的抽象來隱藏日常開發背後的代碼。 – Merwer

+0

這有點麻煩,但如果我不得不經常這樣做,我會實現這個目標。 – Gabe

+0

@Gabe「MyLookup」類已經實現,所以它的每個實例都不需要更多的工作來創建,而實際上使用生成的「查找」要容易得多。是否值得花時間寫「MyLookup」是一個很好的問題,但考慮到它已經完成了,是否使用它的問題更容易回答。 – Servy