我有一份时间表条目列表,通常每个用户在同一天会有多个条目,因为它们在午餐时间进出。
我的时间表类看起来像这样:
public class TimeSheetEntry
{
public Guid EmployeeId { get; set; }
public DateTime ClockInTimeStamp { get; set; }
public DateTime ClockOutTimeStamp { get; set; }
}
我想确定用户工作的天数。我想通过计算ClockInTimeStamp
中的唯一天数,使用List<TimeSheetEntry>()
属性来执行此操作。
如何使用LINQ获取此列表中的唯一天数?
答案 0 :(得分:1)
.Distinct()可能就是你要找的东西。您只需通过Distinct()指定要过滤的属性。
int numberOfDaysWorked = fullCollection.Select(x => x.ClockInTimeStamp).Distinct().Count();
答案 1 :(得分:1)
您是否正在寻找严格计算日期,而不考虑时间?如果是这样,您可能希望查看更改两个DateTime属性类型并使用Noda Time库,该库仅具有日期类型。
鉴于您对我对OP的评论的回应,我强烈建议您远离DateTime并查看Noda Time。
答案 2 :(得分:1)
timesheetEntries
.GroupBy( te => te.EmployeeId )
.Select(
grp => new {
EmployeeId = grp.Key,
DayCount = grp
.Select( te => te.ClockInTimeStamp.Date )
.Distinct()
.Count()
}
)
.ToDictionary(
t => t.EmployeeId,
t => t.DayCount
);
我意识到可以省略中级Select
:
timesheetEntries
.GroupBy( te => te.EmployeeId )
.ToDictionary(
grp => grp.Key /* EmployeeId */,
grp => grp
.Select( te => te.ClockInTimeStamp.Date )
.Distinct()
.Count()
);
答案 3 :(得分:1)
由于时间部分,你可能没有在DateTime上获得明显的分歧。你应该能够通过专门用这样的东西获得DateTime.Date
来获得它:
context.TimeSheetEntries.Select(e => e.ClockInTimeStamp.Date).Distinct();
请注意,如果这是通过EF直接从sql拉出LINQ,那么“.Date”部分可能存在一些问题,因此您可能必须在此之前强制收集枚举。
答案 4 :(得分:1)
我在其他答案中没有看到两个细节:
出于这个原因,我建议从单独的功能开始,以便可以使用各种输入对其进行单元测试。编写单元测试将允许您“伪造”条目的各种组合,考虑您希望结果是什么,测试以确定函数是否输出您期望的,然后轻松调试以找出它没有的原因。 / p>
这是一种方法。由于任何个别日期都可能重复(给定日期可能有任意数量的时间戳),我只是将它们添加到HashSet<DateTime>
,这将消除重复。
然后,如果在开始日期和结束日期之间至少有一整天,则循环显示范围中的日期并添加所有日期。
public static class TimeSheetCalculations
{
public static int CalculateDistinctDays(this IEnumerable<TimeSheetEntry> entries)
{
var uniqueDays = new HashSet<DateTime>();
foreach (var entry in entries)
{
var clockInDate = entry.ClockInTimeStamp.Date;
var clockOutDate = entry.ClockOutTimeStamp.Date;
uniqueDays.Add(clockInDate);
uniqueDays.Add(clockOutDate);
var totalDays = (clockOutDate - clockInDate).TotalDays;
if (totalDays < 2) continue;
for (var x = 1; x < totalDays; x++)
{
uniqueDays.Add(clockInDate.AddDays(x));
}
}
return uniqueDays.Count;
}
}
(我通常不会使用static
方法,但在这种情况下,它似乎无害。)
现在该方法已被隔离,很容易编写一些测试。我写了一篇:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void CalculatesCorrectDistinctDaysWorked()
{
var timesheetEntries = new TimeSheetEntry[]
{
new TimeSheetEntry
{
ClockInTimeStamp = DateTime.Parse("1/1/2018 11:50PM"),
ClockOutTimeStamp = DateTime.Parse("1/5/2018 1:00AM")
},
new TimeSheetEntry
{
ClockInTimeStamp = DateTime.Parse("1/5/2018 1:00PM"),
ClockOutTimeStamp = DateTime.Parse("1/5/2018 9:00PM")
},
new TimeSheetEntry
{
ClockInTimeStamp = DateTime.Parse("1/6/2018 1:00PM"),
ClockOutTimeStamp = DateTime.Parse("1/7/2018 9:00PM")
},
};
var daysWorked = timesheetEntries.CalculateDistinctDays();
Assert.AreEqual(7, daysWorked);
}
}
如果有人用1/1打孔,1/5打孔,1/5再打入和打出,1/6打出,1/7打出,那就是7天,即使他们从未打过在1/2,1 / 3或1/4上打孔或打孔。
单元测试确认预期结果。
我要检查的另一个错误情况是确认时钟输出是在clockin之后。但我不会在这里这样做。我会在其他地方验证这个条目(你可能已经是。)