我有一个对象列表,详细说明了工人的时钟打卡,如下所示:
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.作为一个最基本的步骤,我已经将一系列的冲压转换成了一个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表达式将我的数据重新组织到最终产品中?它可以从原始数据开始,也可以从内部开始。感谢您对此谜题的研究。
发布于 2017-03-18 11:12:58
这是我使用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是几天的结果。看看如何使用它们:
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实现都很幼稚,不能处理有人通宵工作并在几天内产生记录的情况。不过,修复它并不是很难。
https://stackoverflow.com/questions/42868029
复制相似问题