Linq - 分组和多个字段的计数(输出总数)

问题描述:

我有一个查询答案的应用程序(50个字段,名称为upa1,upa2,upd1,upd6等,可能的值为“y”, “n”和“na”)。Linq - 分组和多个字段的计数(输出总数)

我可以和计数这一个单场:

foreach(var y in items.GroupBy(g => g.upa1) 
         .Select(group => new {upa1name = group.Key, upa1count = group.Count()})) { 
    <div>These are the totals for upa1:</div> 
    <div>@y.upa1name : @y.upa1count</div> 
} 

,它会输出这样的:

These are the totals for upa1: 
y : 30 
n : 11 
na : 18 

我可以重复这个手动的所有问题,但我需要算所有问题的“y”,“n”和“na”数量形成单个输出:

These are the totals for all questions: 
y : 1342 
n : 879 
na : 445 

我知道那里已经有数百个关于这些问题的主题,但是信息太过紧张,并且在这个问题上有太多变化(不同的表格,连接等)。没有关于“多个领域的总数”。

谢谢。

此外,在附注中,每条线只在答案至少存在一次时才会出现,但即使它是“y:0”,我也需要它显示。这可能吗?

+1

现在改变它可能有点晚,但更好的数据模型会为你解决这个问题。问题不应该在单独的领域,而应该包含在一系列问题中,每个问题都有一个名称,内容和一系列答案。 – oerkelens

+0

一点也不迟。我可以尝试使用问卷ID,问题,答案等优化格式制作表格。因此,每个问卷将在表中放置50+行,如“1088”,“upa1”,“y”和“1088”,“upa2”,“n”等。 –

+0

或其他任何格式,如果您可以给我例。 –

只有三个可能的答案,你可以在三个不同的查询做到这一点:

foreach (var ans in new[] {"y", "n", "na"}) { 
    var count = items.Sum(item => 
     (item.upa1==ans?1:0) + (item.upa2==ans?1:0) + (item.upd1==ans?1:0) + ... 
    ); 
    <div>These are the totals for @ans:</div> 
    <div>@ans : @count</div> 
} 

这应该运行相当快。

+0

看起来很简单,但它意味着使用自定义控制器。由于我没有真正涉足2sxc自定义API设计的东西,所以只能查看解决方案。 –

+0

@JoãoGomes只要您将所有名称放在比较链中而不是上面的'...'中,这种方法就可以工作。 – dasblinkenlight

+0

太好了。简单,容易,可以理解。喜欢这个解决方案。谢谢。 –

比方说你有一个这样的类:

public class Inquiry 
{ 
    public string UpaName { get; set; } 
    public string UpaValue { get; set; } 
} 

现在让我们来填充该列表与一些测试数据:

var list = new List<Inquiry>() 
{ 
    new Inquiry() { UpaName = "upa1", UpaValue = "y" }, 
    new Inquiry() { UpaName = "upa1", UpaValue = "y" }, 
    new Inquiry() { UpaName = "upa1", UpaValue = "n" }, 
    new Inquiry() { UpaName = "upa1", UpaValue = "na" }, 
    new Inquiry() { UpaName = "upa2", UpaValue = "y" }, 
    new Inquiry() { UpaName = "upa2", UpaValue = "n" }, 
    new Inquiry() { UpaName = "upa2", UpaValue = "na" }, 
    new Inquiry() { UpaName = "upa2", UpaValue = "na" }, 
    new Inquiry() { UpaName = "upa1", UpaValue = "y" }, 
    new Inquiry() { UpaName = "upa1", UpaValue = "y" }, 
    new Inquiry() { UpaName = "upa2", UpaValue = "n" }, 
    new Inquiry() { UpaName = "upa1", UpaValue = "y" }, 
    new Inquiry() { UpaName = "upa1", UpaValue = "y" }, 
}; 

我们可以创建另一个类,将持有我们的关键随着sumarized值。事情是这样的:

[DebuggerDisplay("{UpaName,nq} y:{y,nq} n:{n,nq} na:{na,nq}")]//include Sysytem.Diagnostics 
public class InquirySummary 
{ 
    public string UpaName { get; set; } 
    public int y { get; set; } 
    public int n { get; set; } 
    public int na { get; set; } 
} 

我们可以比实例键值的字典,其中关键是你查询的答案和值将是我们InquirySummary类的一个实例。

var summary = new Dictionary<string, InquirySummary>(); 

我们将定义一个操作,该操作将获取INquiry对象和InquirySummary对象并将总结值。

Action<Inquiry, InquirySummary> sumarize = new Action<Inquiry, InquirySummary>((i, sum) => 
{ 
    if (i.UpaValue == "y") 
     sum.y += 1; 
    else if (i.UpaValue == "n") 
     sum.n += 1; 
    else 
     sum.na += 1; 
}); 

最后,您将拥有所有需要的东西来调用一个只能在列表中运行一次的聚合。

list.Aggregate(summary, (b, c) => 
{ 
    if (summary.ContainsKey(c.UpaName)) { 
     var sum = summary[c.UpaName]; 
     sumarize(c, sum); 
    } 
    else 
    { 
     var sum = new InquirySummary(); 
     summary.Add(c.UpaName, sum); 
     sumarize(c, sum); 
    } 
    return summary; 
}); 

这基本上将我们的字典作为种子,并且比我们开始循环遍历列表。首先如果条件将检查密钥是否已经存在于dicitonary中,并且会简单地将该sumarize动作称为该值。否则,我们会创建一个新的InquirySummary对象,并将他咀嚼。每次我们返回我们的案例词典中的种子。

+0

这离我的理解还有几十年。它将需要控制器的自定义API。更简单的视图解决方案将是首选。 –

+0

不需要自定义API。把它放在扩展方法中,然后在你的视图中调用它。 – Rob