C# 如何使用Linq同时对项目列表进行分组和求和?
我从外部API接收时间条目,并将它们存储在类型的集合中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
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
};