gpt4 book ai didi

c# - 查询 "Memory"的 LINQ

转载 作者:可可西里 更新时间:2023-11-01 09:05:04 26 4
gpt4 key购买 nike

是否 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 };

在上面的例子中,foo1foo2相似但两者都是foo1foo2 类似于 foo3

假设我们有一个 query结果包含 IEnumerableIOrderedEnumerableFoo .来自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;
}
}

topNFoos List将作为之前查询的内存,我们可以跳过 Foo qforeach已经具有相同 Bars 的循环与 AnyFootopNFoos .

我的问题是,在 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、spender 和 David B 使用 IEqualityComparer 的答案特别棒)。不过,如果有人可以回答我更笼统的问题“LINQ 有没有办法在查询时‘记住’它以前的查询结果”,我也很高兴)

(除了我在上面介绍的使用完全/部分 LINQ 时的特定情况下的显着性能差异之外,旨在回答我关于 LINQ 内存的一般问题的一个答案是 Ivan Stoev 的。另一个具有良好组合的答案是 Rob 的. 为了让自己更清楚,我寻找通用且有效的解决方案,如果有的话,使用 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());
}
}
}

你真的可以高效的得到top 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;
}
}

@Rob 上面的方法大致相似,并展示了如何在 LINQ 中直接使用比较器,但请注意我对他的回答所做的评论。

关于c# - 查询 "Memory"的 LINQ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35906301/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com