如何将Linq分组作为参数传递给自定义扩展方法

时间:2016-02-16 14:30:54

标签: c# linq

我有一个Linq查询,我在其中获得一组问题的响应值。我按问题对响应数据进行分组,然后对响应数据执行各种聚合,例如对它们求平均值。我还根据指定响应选项值范围,计算可归类为“高”,“中”和“低”的响应比例。

var result = ItemResponses
        .Where (ir => ir.ItemID < 4 && ir.AssessmentInstance.ProjectID == 5)
        .Select (ir => ir)
        .GroupBy (ir => new {
            ir.ItemID
        }).Select (grouped => new {
            ItemID = grouped.Key.ItemID,
            Average = (double)grouped.Average (g => g.OptionValue),
            ProportionHighScore = 
                (double)grouped.Where(g => g.OptionValue == 5 || g.OptionValue == 6).Count() 
                    / (double)grouped.Count(),
        });

我想移动代码,我指定哪些选项值应该组合成一个远离Linq查询的“高”响应类别,并且正在考虑设置一个扩展方法来执行此操作。在扩展方法中,我可以指定响应选项值的不同组合,我可以将其评分为“高”分数,跨越风格(例如,如果最大响应不是6但是10,那么我会计算选项8,9, 10,朝着“高”回应类别。

扩展方法可能如下所示:

public static double ProportionHighScore(this IGrouping<a,b> values, int ResponseOptionID)
{
    double ret = 0;
    switch (ResponseOptionID)
    {
        case 1:
            //code here to combine response options 5 and 6, and divide by total
            break;
        case 2:
            //code here to combine response options 8, 9 and 10 and divide by total
            break;
        case 3:
            //etc..
            break;
        default:
            break;
    }
    return ret;
}

但我的问题是:如何将Linq分组值作为参数传递给扩展方法? IGrouping b的类型是匿名类型。

更新 我喜欢只做GroupBy (ir => it.ItemID)的想法,以便我可以访问"IGrouping<int, ItemResponse>"。但在这里的代码我简化了一点。在我的实际代码中还有一些事情正在发生,例如,如果项目被标记为“IsReversed”,则反转OptionValue分数。

var result2 = ItemResponses
        .Where (ir => ir.ItemID < 4 && ir.AssessmentInstance.ProjectID == 5)
        .Select (ir => new {
            ItemID = ir.ItemID,
            OptionValue =
              (
                  //Reverse option value of items that are flagged to require reverse scoring
                  ir.Item.IsReversed == 0 ? ir.OptionValue : 
                  ((ir.ResponseScaleOption.ResponseScale.MaxValue + 1) - ir.OptionValue)
              ),
            }) 
        .GroupBy (g => new {g.ItemID})
        .Select (grouped => new {
            ItemID = grouped.Key.ItemID,
            Average = (double)grouped.Average (g => g.OptionValue),
            ProportionHighScore = 
                (double)grouped.Where(g => g.OptionValue == 5 || g.OptionValue == 6).Count() 
                    / (double)grouped.Count(),
        });

在此查询的某些版本中,我还包括来自某些连接表的字段。所以需要反转OptionValues是我认为我需要一个匿名类型的原因之一。也许我需要创建一个可以投影到的新类(“ItemResponseForAggregation”或某个此类名称),然后能够为我的扩展参数执行IGrouping<int, ItemResponseForAggregation>

2 个答案:

答案 0 :(得分:1)

您无法以这种方式传递匿名类型。他们必须处于直接执行环境中,以便将它们视为强类型。创建一个轻量级类型,以便您可以将其传入,然后在参数&#39; b&#39;上添加参数约束。强制它必须是你已经创建的类型。

答案 1 :(得分:0)

我在LINQPad中模拟了你正在寻找的想法(如果我正确理解要求):

void Main()
{
    var ItemResponses = new List<ItemResponse>();

    var result = ItemResponses
        .Where(ir => ir.ItemID < 4 && ir.AssessmentInstance.ProjectID == 5)
        .GroupBy(ir => ir.ItemID)
        .Select(
            grouped => new {
                ItemID = grouped.Key,
                Average = (double)grouped.Average(g => g.OptionValue),
                ProportionHighScore = grouped.ProportionHighScore(1, 2, 3, 4, 5)
            }
        );

    result.Dump();
}

public class ItemResponse 
{
    public int ItemID { get; set; }
    public int OptionValue { get; set; }
    public AssessmentInstanceItem AssessmentInstance { get; set; }
}

public class AssessmentInstanceItem
{
    public int ProjectID  { get; set; }
}

public static class ItemResponseExtensions
{
    public static double ProportionHighScore(this IGrouping<int, ItemResponse> values, params int[] ResponseOptionID)
    {
        double count = 0;
        double total = values.Count();

        foreach (int r in ResponseOptionID)
            count += (double)values.Where(g => g.OptionValue == r).Count();

        return count / total;
    }
}

在扩展方法中,params参数允许您根据需要指定任意数量的选项。然后我只是循环选项,为每个响应选项添加计数。