Jus*_*ant 7 c# linq linq-to-objects group-by row-number
经过大量的Google搜索和代码实验,我对一个复杂的C#LINQ-to-objects问题感到难过,在SQL中使用一对ROW_NUMBER()... PARTITION BY函数和一个或两个子查询很容易解决这个问题.
用语言来说,这就是我在代码中尝试做的事情 - 基本要求是从列表中删除重复的文档:
class Document { string Title; int SourceId; // sources are prioritized (ID=1 better than ID=2) }
步骤#1很简单(例如codepronet.blogspot.com/2009/01/group-by-in-linq.html),但我对步骤#2和#3感到困惑.我似乎无法构建一个无红色波形的C#LINQ查询来解决所有这三个步骤.
Anders Heilsberg关于这个帖子的帖子是我认为如果我能正确理解语法,那么上面的步骤#2和#3的答案.
我倾向于避免使用外部局部变量来执行索引计算,正如slodge.blogspot.com/2009/01/adding-row-number-using-linq-to-objects.html所建议的那样,因为该解决方案中断了如果外部变量被修改.
最佳地,可以首先完成逐个标题步骤,因此"内部"分组(首先由Source计算索引,然后通过索引来过滤掉重复项)可以对每个"按标题"的少量对象进行操作group,因为每个by-title组中的文档数量通常在100以下.我真的不想要N 2解决方案!
我当然可以用嵌套的foreach循环解决这个问题,但看起来像LINQ这个问题应该很简单.
有任何想法吗?
我认为jpbochi错过了你希望你的分组是成对的值(Title + SourceId然后是Title + Index).这是一个LINQ查询(主要)解决方案:
var selectedFew =
from doc in docs
group doc by new { doc.Title, doc.SourceId } into g
from docIndex in g.Select((d, i) => new { Doc = d, Index = i })
group docIndex by new { docIndex.Doc.Title, docIndex.Index } into g
select g.Aggregate((a,b) => (a.Doc.SourceId <= b.Doc.SourceId) ? a : b);
Run Code Online (Sandbox Code Playgroud)
首先我们按Title + SourceId进行分组(我使用匿名类型,因为编译器为分组查找构建了一个好的哈希码).然后我们使用Select将分组的索引附加到文档中,我们在第二个分组中使用它.最后,对于每个组,我们选择最低的SourceId.
鉴于此输入:
var docs = new[] {
new { Title = "ABC", SourceId = 0 },
new { Title = "ABC", SourceId = 4 },
new { Title = "ABC", SourceId = 2 },
new { Title = "123", SourceId = 7 },
new { Title = "123", SourceId = 7 },
new { Title = "123", SourceId = 7 },
new { Title = "123", SourceId = 5 },
new { Title = "123", SourceId = 5 },
};
Run Code Online (Sandbox Code Playgroud)
我得到这个输出:
{ Doc = { Title = ABC, SourceId = 0 }, Index = 0 }
{ Doc = { Title = 123, SourceId = 5 }, Index = 0 }
{ Doc = { Title = 123, SourceId = 5 }, Index = 1 }
{ Doc = { Title = 123, SourceId = 7 }, Index = 2 }
Run Code Online (Sandbox Code Playgroud)
更新:我刚看到你关于按标题分组的问题.您可以使用标题组上的子查询执行此操作:
var selectedFew =
from doc in docs
group doc by doc.Title into titleGroup
from docWithIndex in
(
from doc in titleGroup
group doc by doc.SourceId into idGroup
from docIndex in idGroup.Select((d, i) => new { Doc = d, Index = i })
group docIndex by docIndex.Index into indexGroup
select indexGroup.Aggregate((a,b) => (a.Doc.SourceId <= b.Doc.SourceId) ? a : b)
)
select docWithIndex;
Run Code Online (Sandbox Code Playgroud)