2012-05-29 114 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