2017-03-17 68 views
1

我有一個對象列表,對工人的細節時鐘拳,看起來像這樣:轉換時間拳名單成小時在C#中的工作時間

Person Time  Action 
Cindy 07:00 clockin 
Jim  08:12 clockin 
Cindy 11:15 startlunch 
Cindy 12:12 endlunch 
Jim  12:30 startlunch 
Jim  12:55 endlunch 
Cindy 15:30 clockout 
Jim  17:00 clockout 

當然實際數據是數百人,並被部門打破等

他們希望我創建一份報告,顯示每天每小時工作多少勞動力。最終的結果應該是這樣的:

Time Worked 
7-8  1 
8-9  1.8 
9-10  2 
10-11 2 
11-12 1.25 
12-1  1.3 
etc. 

作爲interum一步,我已經轉換了一系列拳來WorkRecords的列表,它看起來像這樣:

Person  In  Out 
Cindy  07:00 11:15 
Jim  08:22 12:30 
Cindy  12:12 15:30 
Jim  12:55 17:00 

我在想循環通過所有的時間和測試每個WorkRecord,如果它包括在給定的小時內的時間,但這看起來很麻煩。

是否有更優雅的方式來使用lambda或linq表達式將數據重組爲我的最終產品?它可以從原始數據或Interum開始。感謝您查看這個難題。

回答

1

這是我對LINQ的嘗試。

我認爲你的WorkRecord就是這樣的。

public class WorkRecord 
    { 
     public readonly String name; 
     public readonly DateTime StarTime; 
     public readonly DateTime EndTime; 

     public WorkRecord(string name, DateTime starTime, DateTime endTime) 
     { 
      this.name = name; 
      StarTime = starTime; 
      EndTime = endTime; 
     } 

     public WorkRecord(string name, string starTime, string endTime) 
     { 
      this.name = name; 
      StarTime = DateTime.ParseExact(starTime, "HH:mm", null); 
      EndTime = DateTime.ParseExact(endTime, "HH:mm", null); 
     } 
    } 

目前還不清楚它們是否已經過一天的預過濾。我會盡量覆蓋這兩種情況下(但依然簡單)

public class DayStats 
    { 
     public readonly int[] TotalMinutes = new int[24]; 

     public void AddWorkRecord(WorkRecord record) 
     { 
      // Note: this method doesn't handle case when EndTime is next day 
      // handle "middle" hours, they are all full 
      for (int hour = record.StarTime.Hour + 1; hour < record.EndTime.Hour; hour++) 
      { 
       TotalMinutes[hour] += 60; 
      } 
      // handle first and last hours that might be not full 
      if (record.StarTime.Hour == record.EndTime.Hour) 
      { 
       TotalMinutes[record.StarTime.Hour] += record.EndTime.Minute - record.StarTime.Minute; 
      } 
      else 
      { 
       TotalMinutes[record.StarTime.Hour] += 60 - record.StarTime.Minute; 
       TotalMinutes[record.EndTime.Hour] += record.EndTime.Minute; 
      } 
     } 

     public string AsPrettyString() 
     { 
      return string.Join("\n", TotalMinutes 
       .Select((totalMinutes, hour) => string.Format("{0}-{1} {2}", hour, hour + 1, totalMinutes))); 
     } 
    } 

    public class TimeCardAggregate 
    { 
     private readonly Dictionary<DateTime, DayStats> _days = new Dictionary<DateTime, DayStats>(); 

     public void AddWorkRecord(WorkRecord record) 
     { 
      // Note: this method doesn't handle case when EndTime is next day 
      var date = record.StarTime.Date; 
      DayStats dayStats; 
      if (!_days.TryGetValue(date, out dayStats)) 
      { 
       dayStats = new DayStats(); 
       _days.Add(date, dayStats); 
      } 
      dayStats.AddWorkRecord(record); 
     } 

     public List<KeyValuePair<DateTime, DayStats>> GetTimecard() 
     { 
      return _days.ToList().OrderBy(kv => kv.Key).ToList(); 
     } 
    } 

DayStats代表單日彙總的結果。 TimeCardAggregate是幾天的結果。大部分工作都是通過輔助方法AddWorkRecord在兩個類中完成的,您可以從Aggregate LINQ方法中使用這兩個類。看你如何使用它們:

static void Main(string[] args) 
    { 
     List<WorkRecord> records = new List<WorkRecord> 
     { 
      new WorkRecord("Cindy", "07:00", "11:15"), 
      new WorkRecord("Jim", "08:22", "12:30"), 
      new WorkRecord("Jim", "12:12", "15:30"), 
      new WorkRecord("Cindy", "12:55", "17:00") 
     }; 

     var dayStats = records.Aggregate(new DayStats(), (ds, wr) => 
     { 
      ds.AddWorkRecord(wr); 
      return ds; 
     }); 

     Console.WriteLine(dayStats.AsPrettyString()); 



     List<WorkRecord> recordsForTwoDays = new List<WorkRecord>(); 
     recordsForTwoDays.AddRange(records); 
     // just copy the data for the next day 
     recordsForTwoDays.AddRange(records.Select(wr => new WorkRecord(wr.name, wr.StarTime.AddDays(1), wr.StarTime.AddDays(1)))); 
     var timecard = recordsForTwoDays.Aggregate(new TimeCardAggregate(), (ds, wr) => 
     { 
      ds.AddWorkRecord(wr); 
      return ds; 
     }); 
     Console.WriteLine("\n\n"); 


     Console.WriteLine(string.Join("\n-------------\n", timecard.GetTimecard().Select(kv => 
     { 
      return kv.Key.ToShortDateString() + ":\n" + dayStats.AsPrettyString(); 
     }))); 
    } 

還要注意,的AddWorkRecord既實現是幼稚,當有人連夜在不處理的情況下,並有超過數天的紀錄產卵。雖然這不是很難解決。