理论
你请求的函数有点像数学归纳法的证明:如果你已经计算了前 N 个 measureIds 的结果,并称它为 F(N),那么第一个 (N+1) 个 measureIds 的结果是F(N) 与元素 N+1 的“FilterIds”的交集。
换句话说:一旦你计算了measureIds 3、4、7(假设是 X)的函数,那么 measureIds 3、4、7、10 的结果就是属于 'FilterIds' 的交集measureId' 10 和 X。
当您在需求中看到计算的输出是下一个计算的输入以及下一个元素时,函数Queryable.Aggregate 是应该立即在您的脑海中弹出的函数。
回到你的问题
所以你需要一个函数,将前面计算的结果作为一个参数,将下一个元素作为另一个参数:
- 'previousResult',是
FilterIds 的序列。到目前为止,它是“FilterIds”或所有已处理的measureIds 的交集。
- 'nextMeasureId' 是下一个要处理的
measureId。
函数的输出应该是'previousResult'和属于'measureId'的所有'FilterIds'的交集
(previousResult, nextMeasureId) =>
previousResult.Intersect( "all FilterIds belonging to nextMeasureId")
现在要正式化,我需要一个类。
class MeasureResult
{
public int MeasureId {get; set;}
public int FilterId {get; set;}
}
我们创建一个函数,输入IQueryable<MeasureResult>,这是您的原始表格,其他输入:IEnumerable<int> measureIds。
输出是一个int序列,包含所有FilterIds这些measureIds的交集:IEnumerable<int>
我会将它实现为扩展函数,因此它可以用作任何 linq 函数。见Extension Methods Demystified
static IEnumerable<int> IntersectFilterIds(this IQueryable<MeasureResult> measureResults,
IEnumerable<int> measureIds)
{
// Todo: check validity input parameters;
// if no measureIds, return empty sequence:
if (!measureIds.Any()) return Enumerable.Empty<int>();
// TODO: add some Aggregate function
}
Aggregate 的输入是 measureId 的序列:取第一个和第二个 measureId 并相交。取结果与第三个 measureId 相交,取结果与第四个 measureId 相交,以此类推。
聚合的输出是您请求的交集。这与输入序列的元素类型不同:输入元素是整数,输出元素是整数序列。因此,您需要聚合重载,其中结果 (TAccumulate) 与输入序列元素 (TSource) 的类型不同。这个版本需要一个Seed,这是你第一个交集的结果:你的第一个元素。
我们还需要一个函数来获取measureResults 和一个measureId 的表,并返回此measureId 中的所有FilterIds。我会把它写成一个扩展函数:
static IEnumerable<int> GetFilterIds(this IEnumerable<MeasureResult> measureResults,
int measureId)
{
return measureResults.Where(measureResult => measureResult.MeasureId == measureId)
.Select(measureResult.FilterId);
}
该函数用于仅获取第一个元素的交集结果(即第一个元素的所有FilterId。用作Seed。也用于获取下一个元素的FilterId
现在我们有一切可以填充聚合:
- 种子是第一个
measureId的FilterIds
- 聚合的来源是所有其他
measureIds
-
累加函数是previousResult和下一个measureId的FilterIds的交集
静态 IEnumerable IntersectFilterIds(这个 IEnumerable measureResults,
IEnumerable measureIds)
{
if (!measureIds.Any()) return Enumerable.Empty();
int firstMeasureId = measureIds.FirstOrDefault();
IEnumerable<int> seed = measureResults.GetFilterIds(firstMeasureId);
IEnumerable<int> restOfSequence = measureIds.Skip(1);
// the aggregate:
var result = restOfSequence.Aggregate( // aggregate the rest of the measureIds
seed, // start with the FilterIds of the first measureId
(previousResult, nextMeasureId) => // the next accumulation is
previousResult.Intersect( // the intersection of the previous result
measureResults.GetFilterId(nextMeasureId) // and all FilterIds of the next measureId
);
}
我不确定您的 measureResults 和 measureIds 是在本地内存 (IEnumerable) 中还是在数据库的表中。如果是后者,你不能使用函数,所以你必须编写代码而不是函数,这使得它有点难以阅读,这就是我选择函数解释的原因。
IQueryable<MeasureResult> measureResults = ...
IQueryable<int> measureIds = ...
var result = measureIds.Skip(1) // = rest of sequence, also: empty if no measureIds
.Aggregate(
// first parameter: seed, get FilterIds of first measureId
// note that seed is empty sequence if there is no first measureId
measureResults
.Where(measureResult.MeasureId == measureIds.FirstOrDefault())
.Select(measureResult => measureResult.FilterId),
// aggregate function: intersection of the previousResult
// and all FilterIds for the nextMeasureId
(previousResult, nextMeasureId) => previourResult.Intersect(
measureResults.Where(measureResult.MeasureId == nextMeasureId)
.Select(measureResult => measureResult.FilterId));