LINQ 中的条件 GroupBy()
Conditional GroupBy() in LINQ
我正在处理一个充满项目之间相似性的矩阵。我将这些保存为数据库中的对象列表。相似度对象如下所示:
public class Similarity
{
public virtual Guid MatrixId { get; set; } //The id of the matrix the similarity is in
public virtual Guid FirstIndex { get; set; } //The id of the item of the left side of the matrix
public virtual Guid SecondIndex { get; set; } //The id of the item of the top side of the matrix
public virtual double Similarity { get; set; } //The similarity
}
用户可以查看这些项目。我想检索一个项目列表,这些项目是 'similar' 到用户已评论的项目。问题是我无法确定该项目的 id 是在 FirstIndex
还是 SecondIndex
中。我已经编写了一些代码来执行我想要的操作,但我想知道这是否可以在 1 条语句中实现。
var itemsNotReviewed = Similarities.Where(x => !itemsReviewed.Contains(x.SecondIndex))
.GroupBy(x => x.SecondIndex)
.ToList();
itemsNotReviewed.AddRange(Similarities.Where(x => !itemsReviewed.Contains(x.FirstIndex))
.GroupBy(x => x.FirstIndex)
.ToList());
其中 itemsReviewed
是用户评论过的项目的 guid 列表,Similarities
是与用户评论过的项目相似的所有项目的列表。我使用此函数检索该列表:
return (from Row in _context.SimilarityMatrix
where itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex)
select Row)
.Distinct()
.ToList();
其中 itemIds
是用户评论过的项目的 GUID 列表。
有没有办法根据 Where 子句按第一个或第二个索引分组?
如果我需要详细说明,请告诉我!
我会改变你获取原始列表的方式:
_context.SimilarityMatrix.Where(Row => itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex))
.Select(r => new { r.MatrixId, r.FirstIndex, r.SecondIndex, r.Similarity, MatchingIndex = itemIds.Contains(r.FirstIndex) ? r.FirstIndex : r.SecondIndex })
.Distinct()
.ToList();
这样你只需要按匹配索引分组。
var itemsNotReviewed = Similarities.
.GroupBy(x => x.MatchingIndex)
.ToList();
您可能希望在动态对象之后转换为您的相似度 class 或者只是更改 class 以包含匹配索引。
您可以通过以下方式将它们转换为您的相似度类型:
var itemsNotReviewed = Similarities.
.GroupBy(x => x.MatchingIndex)
.Select(g => new { g.Key, Values = g.Values.Select(d => new Similarity { MatrixId = d.MatrixId, FirstIndex = d.FirstIndex, SecondIndex = d.SecondIndex, Similarity = d.Similarity }).ToList() })
.ToList();
怎么样
(from x in Similarities
let b2 = !itemsReviewed.Contains(x.SecondIndex)
let b1 = !itemsReviewed.Contains(x.FirstIndex)
where b1 || b2
groupby b2 ? x.SecondIndex : x.FirstIndex into grp
select grp)
.ToList()
let 语句引入了一个新的临时变量来存储布尔值。您当然也可以内联其他函数:
(from x in (from Row in _context.SimilarityMatrix
where itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex)
select Row)
.Distinct()
.ToList()
let b2 = !itemsReviewed.Contains(x.SecondIndex)
let b1 = !itemsReviewed.Contains(x.FirstIndex)
where b1 || b2
groupby b2 ? x.SecondIndex : x.FirstIndex into group
select group)
.ToList()
如果您想使用非 LINQ 语法,您可能需要引入一些匿名类型:
Similarities
.Select(s => new
{
b2 = !itemsReviewed.Contains(x.SecondIndex),
b1 = !itemsReviewed.Contains(x.FirstIndex),
s
})
.Where(a => a.b1 || a.b2)
.GroupBy(a => a.b2 ? a.s.SecondIndex : a.s.FirstIndex, a => a.x) //edit: to get same semantics, you of course also need the element selector
.ToList()
据我了解,您有一个 Similarity
的列表,其中保证包含 FirstIndex
或 SecondIndex
包含在 itemsReviewed
的 [=16] 列表中的项目=].并且您需要获取 not 中包含的任一索引的元素(如果有的话) itemsReviewed
(由于第一个约束,它可能只是其中一个)并按该元素分组指数.
上面的直接 LINQ 翻译是这样的:
var itemsNotReviewed = Similarities
.Where(item => !itemsReviewed.Contains(item.FirstIndex) || !itemsReviewed.Contains(item.SecondIndex))
.GroupBy(item => !itemsReviewed.Contains(item.FirstIndex) ? item.FirstIndex : item.SecondIndex)
.ToList();
但它包含重复的 itemsReviewed.Contains
检查,这会对性能产生负面影响。
所以更好的变体是引入中间变量,最简单的方法是查询语法和 let
子句:
var itemsNotReviewed =
(from item in Similarities
let index = !itemsReviewed.Contains(item.FirstIndex) ? 1 :
!itemsReviewed.Contains(item.SecondIndex) ? 2 : 0
where index != 0
group item by index == 1 ? item.FirstIndex : item.SecondIndex)
.ToList();
我正在处理一个充满项目之间相似性的矩阵。我将这些保存为数据库中的对象列表。相似度对象如下所示:
public class Similarity
{
public virtual Guid MatrixId { get; set; } //The id of the matrix the similarity is in
public virtual Guid FirstIndex { get; set; } //The id of the item of the left side of the matrix
public virtual Guid SecondIndex { get; set; } //The id of the item of the top side of the matrix
public virtual double Similarity { get; set; } //The similarity
}
用户可以查看这些项目。我想检索一个项目列表,这些项目是 'similar' 到用户已评论的项目。问题是我无法确定该项目的 id 是在 FirstIndex
还是 SecondIndex
中。我已经编写了一些代码来执行我想要的操作,但我想知道这是否可以在 1 条语句中实现。
var itemsNotReviewed = Similarities.Where(x => !itemsReviewed.Contains(x.SecondIndex))
.GroupBy(x => x.SecondIndex)
.ToList();
itemsNotReviewed.AddRange(Similarities.Where(x => !itemsReviewed.Contains(x.FirstIndex))
.GroupBy(x => x.FirstIndex)
.ToList());
其中 itemsReviewed
是用户评论过的项目的 guid 列表,Similarities
是与用户评论过的项目相似的所有项目的列表。我使用此函数检索该列表:
return (from Row in _context.SimilarityMatrix
where itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex)
select Row)
.Distinct()
.ToList();
其中 itemIds
是用户评论过的项目的 GUID 列表。
有没有办法根据 Where 子句按第一个或第二个索引分组?
如果我需要详细说明,请告诉我!
我会改变你获取原始列表的方式:
_context.SimilarityMatrix.Where(Row => itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex))
.Select(r => new { r.MatrixId, r.FirstIndex, r.SecondIndex, r.Similarity, MatchingIndex = itemIds.Contains(r.FirstIndex) ? r.FirstIndex : r.SecondIndex })
.Distinct()
.ToList();
这样你只需要按匹配索引分组。
var itemsNotReviewed = Similarities.
.GroupBy(x => x.MatchingIndex)
.ToList();
您可能希望在动态对象之后转换为您的相似度 class 或者只是更改 class 以包含匹配索引。
您可以通过以下方式将它们转换为您的相似度类型:
var itemsNotReviewed = Similarities.
.GroupBy(x => x.MatchingIndex)
.Select(g => new { g.Key, Values = g.Values.Select(d => new Similarity { MatrixId = d.MatrixId, FirstIndex = d.FirstIndex, SecondIndex = d.SecondIndex, Similarity = d.Similarity }).ToList() })
.ToList();
怎么样
(from x in Similarities
let b2 = !itemsReviewed.Contains(x.SecondIndex)
let b1 = !itemsReviewed.Contains(x.FirstIndex)
where b1 || b2
groupby b2 ? x.SecondIndex : x.FirstIndex into grp
select grp)
.ToList()
let 语句引入了一个新的临时变量来存储布尔值。您当然也可以内联其他函数:
(from x in (from Row in _context.SimilarityMatrix
where itemIds.Contains(Row.FirstIndex) || itemIds.Contains(Row.SecondIndex)
select Row)
.Distinct()
.ToList()
let b2 = !itemsReviewed.Contains(x.SecondIndex)
let b1 = !itemsReviewed.Contains(x.FirstIndex)
where b1 || b2
groupby b2 ? x.SecondIndex : x.FirstIndex into group
select group)
.ToList()
如果您想使用非 LINQ 语法,您可能需要引入一些匿名类型:
Similarities
.Select(s => new
{
b2 = !itemsReviewed.Contains(x.SecondIndex),
b1 = !itemsReviewed.Contains(x.FirstIndex),
s
})
.Where(a => a.b1 || a.b2)
.GroupBy(a => a.b2 ? a.s.SecondIndex : a.s.FirstIndex, a => a.x) //edit: to get same semantics, you of course also need the element selector
.ToList()
据我了解,您有一个 Similarity
的列表,其中保证包含 FirstIndex
或 SecondIndex
包含在 itemsReviewed
的 [=16] 列表中的项目=].并且您需要获取 not 中包含的任一索引的元素(如果有的话) itemsReviewed
(由于第一个约束,它可能只是其中一个)并按该元素分组指数.
上面的直接 LINQ 翻译是这样的:
var itemsNotReviewed = Similarities
.Where(item => !itemsReviewed.Contains(item.FirstIndex) || !itemsReviewed.Contains(item.SecondIndex))
.GroupBy(item => !itemsReviewed.Contains(item.FirstIndex) ? item.FirstIndex : item.SecondIndex)
.ToList();
但它包含重复的 itemsReviewed.Contains
检查,这会对性能产生负面影响。
所以更好的变体是引入中间变量,最简单的方法是查询语法和 let
子句:
var itemsNotReviewed =
(from item in Similarities
let index = !itemsReviewed.Contains(item.FirstIndex) ? 1 :
!itemsReviewed.Contains(item.SecondIndex) ? 2 : 0
where index != 0
group item by index == 1 ? item.FirstIndex : item.SecondIndex)
.ToList();