Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用Linq同时对项目列表进行分组和求和?_C#_Linq - Fatal编程技术网

C# 如何使用Linq同时对项目列表进行分组和求和?

C# 如何使用Linq同时对项目列表进行分组和求和?,c#,linq,C#,Linq,我从外部API接收时间条目,并将它们存储在类型的集合中 public sealed record TimeEntry { public string ActivityName { get; } public float HoursSpent { get; } } 我想按活动对条目进行分组,计算花费的小时数,并得到与其他活动相比的百分比 例如,第1组有23小时,第2组有77小时,第1组应该有23%的百分比,第2组应该有77%,因为总共有100小时 我是从这个开始的 var act

我从外部API接收时间条目,并将它们存储在类型的集合中

public sealed record TimeEntry
{
    public string ActivityName { get; }
    public float HoursSpent { get; }
}
我想按活动对条目进行分组,计算花费的小时数,并得到与其他活动相比的百分比

例如,第1组有23小时,第2组有77小时,第1组应该有23%的百分比,第2组应该有77%,因为总共有100小时

我是从这个开始的

var activitiesWithSpentHours = timeEntries
    .GroupBy(timeEntry => timeEntry.ActivityName)
    .Select(activityGroup => new
    {
        Activity = activityGroup.Key,
        HoursSpent = activityGroup.Sum(timeEntry => timeEntry.HoursSpent)
        Percentage = 0 /* calculate here */
    });

如何计算百分比并将其添加到退货类型?我必须把当前项目与整个列表中的其他项目进行比较。Linq是否提供任何功能?

没有理由不能在
选择中引用外部表。这不是很有效,也有点难看,但这可能会奏效:

var activitiesWithSpentHours = timeEntries
    .GroupBy(timeEntry => timeEntry.ActivityName)
    .Select(activityGroup => new
    {
        Activity = activityGroup.Key,
        HoursSpent = activityGroup.Sum(timeEntry => timeEntry.HoursSpent),
        Percentage = (activityGroup.Sum(timeEntry => timeEntry.HoursSpent) * 100) / timeEntries.Sum(timeEntry => timeEntry.HoursSpent)
    });

可能会有更多的性能方法,但在您的情况下,这可能不是问题。

我认为最简单的方法是将小组之前的所有小时相加,然后在select上使用,如下所示:

var sumHoursSpent = timeEntries.Sum(timeEntry => timeEntry.HoursSpent);
var activitiesWithSpentHours = timeEntries
    .GroupBy(timeEntry => timeEntry.ActivityName)
    .Select(activityGroup => new
    {
        Activity = activityGroup.Key,
        HoursSpent = activityGroup.Sum(timeEntry => timeEntry.HoursSpent),
        HoursSpentPercent = activityGroup.Sum(timeEntry => timeEntry.HoursSpent) / sumHoursSpent 
    });
如果你不想对小组进行两次求和,你可以分两个阶段进行:

var activitiesWithSpentHours = timeEntries
    .GroupBy(timeEntry => timeEntry.ActivityName)
    .Select(activityGroup => new
    {
        Activity = activityGroup.Key,
        HoursSpent = activityGroup.Sum(timeEntry => timeEntry.HoursSpent)
    });
var sumHoursSpent = timeEntries.Sum(timeEntry => timeEntry.HoursSpent);
var activitiesWithSpentHoursAndPercent = activitiesWithSpentHours.Select(activityGroup => new
    {
        Activity = activityGroup.Activity ,
        HoursSpent = activityGroup.HoursSpent,
        HoursSpentPercent = activityGroup.HoursSpent / sumHoursSpent
    });

这是一个更为普遍的问题的具体例子,即“如何在单个序列上执行多个计算?”正如Yair I所指出的,最简单(通常也足够)的答案是多次评估序列。在本例中,您首先计算所花费的总小时数,然后执行
GroupBy
。然而,这是低效的。如果您想通过一次传递完成所有操作,那么可以使用
Aggregate
。这是Linq中的通用折叠操作

代码如下(未经测试):

var ActivitiesWithPenthours=timeEntries.Aggregate(
(名称:newdictionary(),总计:0.0),//种子值
(累计,活动)=>
{
累计.byName[activity.ActivityName]+=activity.HoursWasted;
返回值(累计.byName、累计.total+活动.HoursWasted);
},
累计=>accumulated.byName.Select(kv=>new
{
活动=千伏键,
小时消耗=千伏值,
小时支出百分比=千伏值/累计总额
}));
您可能会发现linq在这方面很有用:

var activitiesWithSpentHours = 
  from entry in timeEntries
  group entry by entry.ActivityName into grouping
  let totalSpent = timeEntries.Sum(e => e.HoursSpent)
  let spent = grouping.Sum(e => e.HoursSpent)
  select new {
    ActivityName = grouping.Key,
    HoursSpent = spent,
    Percentage = spent / totalSpent * 100
  };
如果您对第三方解决方案持开放态度,您可以尝试使用Windows对窗口功能的支持:

var activitiesWithSpentHours = 
  from entry in timeEntries
  group entry by entry.ActivityName into grouping
  let spent = grouping.Sum(e => e.HoursSpent)
  let totalSpent = Sql.Ext.Sum(spent).Over().ToValue()
  select new {
    ActivityName = grouping.Key,
    HoursSpent = spent,
    Percentage = spent * 100 / totalSpent
  };

其他人给出了代码示例的回答,但我想知道您是否考虑过这样一种想法,即可以先计算花费的总小时数,然后在linq查询中使用该总小时数。。只是一个建议。
var activitiesWithSpentHours = 
  from entry in timeEntries
  group entry by entry.ActivityName into grouping
  let spent = grouping.Sum(e => e.HoursSpent)
  let totalSpent = Sql.Ext.Sum(spent).Over().ToValue()
  select new {
    ActivityName = grouping.Key,
    HoursSpent = spent,
    Percentage = spent * 100 / totalSpent
  };