[英]c# / LINQ - Average the number of enquiries per day, week and month over a time period
[英]Summing hours worked for a week, over the month - c# & LINQ
我想知道如何简化我的查询,因为我知道我正在执行不好的做法并且在事情上走了很长一段路。
基本上,我每周都会将轮班输入到应用程序中,并且我正在计算每周的工作时间。 我想知道如何根据已输入应用程序 (ShiftDate) 的已知班次获取一周的开始日期和一周的结束日期。
下面是 4 周的快速示例,其中我使用 moment.js 计算出一周的开始和结束,并将它们传递给 c# 函数(由 vStart、vEnd 等表示)。
我正在努力如何获得包含 x.ShiftDate 的一周的开始和结束
Week1 = pg.Sum(x => x.ShiftDate >= vStart1 && x.ShiftDate <= vEnd1 ? x.HoursWorked : 0),
Week2 = pg.Sum(x => x.ShiftDate >= vStart2 && x.ShiftDate <= vEnd2 ? x.HoursWorked : 0),
Week3 = pg.Sum(x => x.ShiftDate >= vStart3 && x.ShiftDate <= vEnd3 ? x.HoursWorked : 0),
Week4 = pg.Sum(x => x.ShiftDate >= vStart4 && x.ShiftDate <= vEnd4 ? x.HoursWorked : 0),
我想弄清楚如何只编写一次 sum 函数,而不必在一个月内每周重复
如果没有您的类数据对象代码,这就是我想出的:
namespace ShiftWork
{
public class UnitTest1
{
[Fact]
public void Test1()
{
DateTime vStart1 = DateTime.Now;
DateTime vEnd1 = vStart1.AddDays(6);
DateTime vStart2 = vEnd1.AddDays(1);
DateTime vEnd2 = vStart2.AddDays(6);
DateTime vStart3 = vEnd2.AddDays(1);
DateTime vEnd3 = vStart3.AddDays(6);
DateTime vStart4 = vEnd3.AddDays(1);
DateTime vEnd4 = vStart4.AddDays(6);
var startAndEnd = new List<Tuple<DateTime, DateTime>>();
startAndEnd.Add(new Tuple<DateTime, DateTime>(vStart1, vEnd1));
startAndEnd.Add(new Tuple<DateTime, DateTime>(vStart2, vEnd2));
startAndEnd.Add(new Tuple<DateTime, DateTime>(vStart3, vEnd3));
startAndEnd.Add(new Tuple<DateTime, DateTime>(vStart4, vEnd4));
var pg = new List<Data>();
pg.Add(new Data() { ShiftDate = vStart1, HoursWorked = 1 });
pg.Add(new Data() { ShiftDate = vStart2, HoursWorked = 3 });
pg.Add(new Data() { ShiftDate = vStart3, HoursWorked = 5 });
pg.Add(new Data() { ShiftDate = vStart3, HoursWorked = 7 });
var Week1 = pg.Sum(x => x.ShiftDate >= vStart1 && x.ShiftDate <= vEnd1 ? x.HoursWorked : 0);
var Week2 = pg.Sum(x => x.ShiftDate >= vStart2 && x.ShiftDate <= vEnd2 ? x.HoursWorked : 0);
var Week3 = pg.Sum(x => x.ShiftDate >= vStart3 && x.ShiftDate <= vEnd3 ? x.HoursWorked : 0);
var Week4 = pg.Sum(x => x.ShiftDate >= vStart4 && x.ShiftDate <= vEnd4 ? x.HoursWorked : 0);
var hoursWorked = pg.Where(x => startAndEnd.Any(dates => dates.Item1 <= x.ShiftDate && dates.Item2 >= x.ShiftDate)).Sum(x => x.HoursWorked);
Assert.Equal(16,hoursWorked);
// delete the first week from criteria
startAndEnd.Remove(startAndEnd.First());
hoursWorked = pg.Where(x => startAndEnd.Any(dates => dates.Item1 <= x.ShiftDate && dates.Item2 >= x.ShiftDate)).Sum(x => x.HoursWorked);
// should be 15 because week one deleted
Assert.Equal(15, hoursWorked);
}
}
public class Data
{
public DateTime ShiftDate { get; set; }
public decimal HoursWorked { get; set; }
}
}
首先,我建议将 sum 运算中的三元运算符替换为 filter + sum 运算:
.Sum( ? : )
:.Sum(x => x.ShiftDate >= vStart* && x.ShiftDate <= vEnd* ? x.HoursWorked : 0)
.Where( ).Sum( )
:.Where(x => x.ShiftDate >= vStart* && x.ShiftDate <= vEnd*)
.Sum(x => x.HoursWorked)
.Sum()
如果集合为空,则返回0
。
避免代码重复的一种选择是创建一个扩展方法来处理过滤+求和:
public static int GetHoursWorked<PgClass>(this IEnumerable<PgClass> source,
DateTime start, DateTime end)
{
return source
.Where(x => x.ShiftDate >= start && x.ShiftDate <= end)
.Sum(x => x.HoursWorked);
}
并按如下方式调用它:
var week1 = pg.GetHoursWorked(vStart1, vEnd1);
var week2 = pg.GetHoursWorked(vStart2, vEnd2);
在上面的例子中,我假设pg
是类PgClass
的一些集合,例如List<PgClass>
。
可能进一步改进的想法:
如果您在一个列表中收集所有关联的vStart*
和vEnd*
值,您可以为该列表中的每个对象选择pg.GetHoursWorked()
,例如创建一个字典,其中周数是键,工作时间是值。
你可以这样做:
var weeklyHours =
from data in SomeList
group t by new { WeekNumber = (t.SomeTargetDate - firstDay).Days / 7} into weeks
select new
{
WeekNumber = weeks.Key.WeekNumber,
Minutes = weeks.Sum(t => t.Minutes) // here your logic of how minutes are calculated can come
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.