c# – LINQ查询“内存”
LINQ有没有办法“记住”以前的查询结果查询?
考虑以下情况: public class Foo { public int Id { get; set; } public ICollection<Bar> Bars { get; set; } } public class Bar { public int Id { get; set; } } 现在,如果两个或更多的Foo有相同的Bar收集(不管是什么顺序),它们被认为是类似的Foo. 例: foo1.Bars = new List<Bar>() { bar1,bar2 }; foo2.Bars = new List<Bar>() { bar2,bar1 }; foo3.Bars = new List<Bar>() { bar3,bar1,bar2 }; 在上述情况下,foo1类似于foo2,但foo1和foo2与foo2不相似 假设我们有一个包含IEnumerable或IOrderedEnumerable的Foo的查询结果.从查询中,我们要找到不相似的第一个N foo. 这个任务似乎需要在之前选择的条的集合的记忆. 使用部分LINQ,我们可以这样做: private bool areBarsSimilar(ICollection<Bar> bars1,ICollection<Bar> bars2) { return bars1.Count == bars2.Count && //have the same amount of bars !bars1.Select(x => x.Id) .Except(bars2.Select(y => y.Id)) .Any(); //and when excepted does not return any element mean similar bar } public void somewhereWithQueryResult(){ . . List<Foo> topNFoos = new List<Foo>(); //this serves as a memory for the previous query int N = 50; //can be any number foreach (var q in query) { //query is IOrderedEnumerable or IEnumerable if (topNFoos.Count == 0 || !topNFoos.Any(foo => areBarsSimilar(foo.Bars,q.Bars))) topNFoos.Add(q); if (topNFoos.Count >= N) //We have had enough Foo break; } } topNFoos列表将作为上一个查询的记忆,我们可以跳过foreach循环中的Foo q,该循环已经具有与顶部NFo中的任何Foo相同的条. 我的问题是在LINQ(完全LINQ)有什么办法吗? var topNFoos = from q in query //put something select q; 如果所需的“内存”来自特定查询项目q或查询外部的变量,那么我们可以使用let变量来缓存它: int index = 0; var topNFoos = from q in query let qc = index++ + q.Id //depends on q or variable outside like index,then it is OK select q; 但是如果它必须来自查询本身的先前查询,那么事情开始变得更麻烦了. 有什么办法吗? 编辑: (我目前是creating a test case(github链接)的答案,仍然在弄清楚如何公正地测试所有的答案) (下面的大部分答案都是为了解决我的特定问题,本身就很好(Rob’s,Spender和David B的使用IEqualityComparer的答案尤其棒极了)然而,如果有人能够回答我更普遍的问题“LINQ有一种方法可以”记住“其查询结果,同时查询”,我也很高兴) (除了上面使用完整/部分LINQ的上述特殊情况下的性能方面的显着差异,一个旨在回答我关于LINQ内存的一般问题的答案是Ivan Stoev,另一个与Rob的组合很好,为了让自己更清楚,我寻找一般高效的解决方案,如果有的话,使用LINQ) 解决方法
所以,这是可能的.但这远不如执行代码.
var res = query.Select(q => new { original = q,matches = query.Where(innerQ => areBarsSimilar(q.Bars,innerQ.Bars)) }).Select(g => new { original = g,joinKey = string.Join(",",g.matches.Select(m => m.Id)) }) .GroupBy (g => g.joinKey) .Select(g => g.First().original.original) .Take(N); 这假设Ids对于每个Foo是唯一的(我也可以使用他们的GetHashCode()). 一个更好的解决方案是保持您所做的工作,或实现自定义比较器,如下所示: 注意:正如@spender的评论所指出的,下面的Equals和GetHashCode将不适用于具有重复项的集合.参考他们的答案更好的实现 – 但是,使用代码将保持不变 class MyComparer : IEqualityComparer<Foo> { public bool Equals(Foo left,Foo right) { return left.Bars.Count() == right.Bars.Count() && //have the same amount of bars left.Bars.Select(x => x.Id) .Except(right.Bars.Select(y => y.Id)) .ToList().Count == 0; //and when excepted returns 0,mean similar bar } public int GetHashCode(Foo foo) { unchecked { int hc = 0; if (foo.Bars != null) foreach (var p in foo.Bars) hc ^= p.GetHashCode(); return hc; } } } 然后您的查询变得简单: var res = query .GroupBy (q => q,new MyComparer()) .Select(g => g.First()) .Take(N); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |