微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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);

原文地址:https://www.jb51.cc/csharp/95886.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐