使用LINQ的不同日子

时间:2018-02-20 23:21:08

标签: c# linq

我有一份时间表条目列表,通常每个用户在同一天会有多个条目,因为它们在午餐时间进出。

我的时间表类看起来像这样:

public class TimeSheetEntry
{
    public Guid EmployeeId { get; set; }
    public DateTime ClockInTimeStamp { get; set; }
    public DateTime ClockOutTimeStamp { get; set; }
}

我想确定用户工作的天数。我想通过计算ClockInTimeStamp中的唯一天数,使用List<TimeSheetEntry>()属性来执行此操作。

如何使用LINQ获取此列表中的唯一天数?

5 个答案:

答案 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)

我在其他答案中没有看到两个细节:

  1. 如果用户在某一天上班并在第二天退出时怎么办?那是两天。
  2. 如果用户在几天后进入并退出,该怎么办?我敢肯定不应该发生,但如果它发生了怎么办?我确定你想要某种方法来确保员工不会在五天内保持计时,但是返回错误结果的功能将无法实现。您可以在其他地方检查。
  3. 出于这个原因,我建议从单独的功能开始,以便可以使用各种输入对其进行单元测试。编写单元测试将允许您“伪造”条目的各种组合,考虑您希望结果是什么,测试以确定函数是否输出您期望的,然后轻松调试以找出它没有的原因。 / 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之后。但我不会在这里这样做。我会在其他地方验证这个条目(你可能已经是。)