有LINQ没有办法在查询时 "记住"以前的查询结果?
考虑以下情况:
public class Foo {
public int Id { get; set; }
public ICollection<Bar> Bars { get; set; }
}
public class Bar {
public int Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
现在,如果两个或更多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 };
Run Code Online (Sandbox Code Playgroud)
在上述情况下,foo1是相似于foo2但两者foo1和foo2是不 相似于foo3
因为我们有一个query结果由IEnumerable或IOrderedEnumerable的Foo.从query,我们要找到第一N foo这是不 相似.
此任务似乎需要bars以前选择的集合的记忆.
有部分 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;
}
}
Run Code Online (Sandbox Code Playgroud)
在topNFoos List将作为上一个查询的内存,我们可以跳过Foo q在foreach其中已经有相同的环Bars带Any的Foo在topNFoos.
我的问题是,有没有办法LINQ(完全 LINQ)?
var topNFoos = from q in query
//put something
select q;
Run Code Online (Sandbox Code Playgroud)
如果所需的"内存"来自特定查询项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;
Run Code Online (Sandbox Code Playgroud)
但如果它必须来自先前查询查询本身,那么事情开始变得更加麻烦.
有没有办法做到这一点?
编辑:
(我目前正在为答案创建一个测试用例(github链接).还在弄清楚如何公平地测试所有答案)
(下面的大部分答案都是为了解决我的特定问题,并且本身就很好(Rob's,Spender's和David B的答案使用得IEqualityComparer特别棒).但是,如果有人能够回答我更普遍的问题" LINQ是否有办法"记住"其先前的查询结果,同时查询",我也很高兴)
(除了我在使用完全/部分LINQ时所呈现的特定情况的性能上的显着差异,一个答案旨在回答我关于LINQ内存的一般问题是Ivan Stoev's.另一个具有良好组合的是Rob's.为了让自己更清楚,我寻找一般和有效的解决方案,如果有,使用LINQ)
我不打算直接回答你的问题,而是提出一种方法,它可以非常有效地过滤前N个非相似项目.
首先,考虑编写一个IEqualityComparer<Foo>使用Bars集合来衡量相等性的东西.在这里,我假设列表可能包含重复的条目,因此对相似性有相当严格的定义:
public class FooSimilarityComparer:IEqualityComparer<Foo>
{
public bool Equals(Foo a, Foo b)
{
//called infrequently
return a.Bars.OrderBy(bar => bar.Id).SequenceEqual(b.Bars.OrderBy(bar => bar.Id));
}
public int GetHashCode(Foo foo)
{
//called frequently
unchecked
{
return foo.Bars.Sum(b => b.GetHashCode());
}
}
}
Run Code Online (Sandbox Code Playgroud)
N通过使用HashSet上面的IEqualityComparer,您可以真正高效地获取顶级非相似项:
IEnumerable<Foo> someFoos; //= some list of Foo
var hs = new HashSet<Foo>(new FooSimilarityComparer());
foreach(var f in someFoos)
{
hs.Add(f); //hashsets don't add duplicates, as measured by the FooSimilarityComparer
if(hs.Count >= 50)
{
break;
}
}
Run Code Online (Sandbox Code Playgroud)
@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);
Run Code Online (Sandbox Code Playgroud)
这假设Id每个 Foo 的 s 都是唯一的(GetHashCode()我想你也可以使用它们的 )。
更好的解决方案是保留您所做的事情,或者实现自定义比较器,如下所示:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后你的查询就变得简单了:
var res = query
.GroupBy (q => q, new MyComparer())
.Select(g => g.First())
.Take(N);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1055 次 |
| 最近记录: |