2017-03-17 3 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 

実際のデータは、何百人もの人々と、それであります部門などで分けられます。

私は、1日にどれくらいの労力が働くかを示すレポートを作成したいと考えています。最終結果は、次のようになります。

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式を使用してデータを最終製品に再編成するよりエレガントな方法はありますか?それは元のデータまたは暫定から始めることができます。このパズルを見ていただきありがとうございます。

答えて

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); 
     } 
    } 

すでに1日前にフィルタリングされているかどうかは不明です。私は一日の集計結果を表し

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は数日間の結果です。ほとんどの作業は、Aggregate LINQメソッドから使​​用できる両方のクラスのヘルパーメソッドAddWorkRecordによって行われます。あなたがそれらを使用する方法を参照してください。

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の両方の実装はナイーブであり、誰かが一晩働いたケースを処理し、数日間にわたって記録産卵を持っていないこと。しかし、それを修正することは非常に難しいことではありません。

関連する問題