Reputation: 337
What I'm trying to do is, if you look below in the code, write a Linq statement that will return the group of ShiftEvents. A group is defined by 1.) EmpId, 2.) EndDate/Time of one ShiftEvent = StartDate/Time of the next ShiftEvent
I want to then create a Shift object where I put the group of ShiftEvents in Shift.List<ShiftEvents>.
I've got a half assed written Linq statement that does seem to join the ShiftEvents..it's the grouping portion I'm completely lost on.
class ShiftEvent{
public int EmpId { get; set; }
public string Activity { get; set; }
public string StartDate { get; set; }
public string StartTime { get; set; }
public string EndDate { get; set; }
public string EndTime { get; set; }
}
class Shift{
public List<ShiftEvent> ShiftEvents { get; set; }
public int EmployeeId { get; set; }
public DateTime StartOfShiftDate { get; set; }
public DateTime StartOfShiftTime { get; set; }
public DateTime EndOfShiftDate { get; set; }
public DateTime EndOfShiftTime { get; set; }
}
void Main()
{
List<ShiftEvent> se = new List<ShiftEvent>();
// Group 1
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/21", StartTime="23:00",EndDate="2015/01/21",EndTime="23:30"});
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/21", StartTime="23:30",EndDate="2015/01/22",EndTime="00:30"});
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/22", StartTime="00:30",EndDate="2015/01/22",EndTime="05:30"});
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/22", StartTime="05:30",EndDate="2015/01/22",EndTime="06:30"});
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/22", StartTime="06:30",EndDate="2015/01/22",EndTime="07:00"});
// Group 2
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/22", StartTime="07:15",EndDate="2015/01/22",EndTime="09:00"});
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/22", StartTime="09:00",EndDate="2015/01/22",EndTime="10:00"});
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/22", StartTime="10:00",EndDate="2015/01/22",EndTime="11:00"});
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/22", StartTime="11:00",EndDate="2015/01/22",EndTime="12:00"});
se.Add(new ShiftEvent {EmpId=1, Activity="Test", StartDate="2015/01/22", StartTime="12:00",EndDate="2015/01/22",EndTime="13:00"});
// Group 3
se.Add(new ShiftEvent {EmpId=2, Activity="Test", StartDate="2015/01/22", StartTime="07:15",EndDate="2015/01/22",EndTime="09:00"});
se.Add(new ShiftEvent {EmpId=2, Activity="Test", StartDate="2015/01/22", StartTime="09:00",EndDate="2015/01/22",EndTime="10:00"});
se.Add(new ShiftEvent {EmpId=2, Activity="Test", StartDate="2015/01/22", StartTime="10:00",EndDate="2015/01/22",EndTime="11:00"});
se.Add(new ShiftEvent {EmpId=2, Activity="Test", StartDate="2015/01/22", StartTime="11:00",EndDate="2015/01/22",EndTime="12:00"});
se.Add(new ShiftEvent {EmpId=2, Activity="Test", StartDate="2015/01/22", StartTime="12:00",EndDate="2015/01/22",EndTime="13:00"});
var obj = from s1 in se
join s2 in se
on new {EmpId = s1.EmpId, Date = s1.EndDate, Time = s1.EndTime} equals new {EmpId = s2.EmpId, Date=s2.StartDate, Time = s2.StartTime}
select s1;
var shiftList = new List<Shift>();
foreach(var shiftEvent in obj){
var shift = new Shift();
shift.ShiftEvent.Add(shiftEvent);
shift.EmployeeId = shiftEvent.EmpId;
.......
shiftList.Add(shift);
}
}
Any pointers would be great!
Upvotes: 1
Views: 93
Reputation: 38518
You can start by grouping the shift events by employees:
var employeeShifts = _shiftEvents.GroupBy(item => item.EmpId);
Then iterate on them to create shifts. Create the first shift with the first shift event. If the next shift event starts when the last one ended, update the end date of the shift. If there is a gap, add the shift to a list and start a new shift with the current one.
Here's the whole solution. It got a little messy because you keep time info as string.
var employeeShifts = _shiftEvents.GroupBy(item => item.EmpId);
List<Shift> shifts = new List<Shift>();
foreach (IGrouping<int, ShiftEvent> employeeShift in employeeShifts)
{
var orderedShiftEvents = employeeShift.Select(item => new { ShiftEvent = item, Interval = item.GetShiftInterval() })
.OrderBy(item => item.Interval.Item1);
Shift currentShift = null;
foreach (var shiftEvent in orderedShiftEvents)
{
if (currentShift == null)
{
currentShift = shiftEvent.ShiftEvent.StartShift(shiftEvent.Interval);
continue;
}
if (currentShift.EndOfShiftDate == shiftEvent.Interval.Item1)
{
currentShift.EndOfShiftDate = shiftEvent.Interval.Item2;
currentShift.ShiftEvents.Add(shiftEvent.ShiftEvent);
}
else
{
shifts.Add(currentShift);
currentShift = shiftEvent.ShiftEvent.StartShift(shiftEvent.Interval);
}
}
shifts.Add(currentShift);
}
Extension methods I had to use:
public static class ShiftEventExtensions
{
public static Tuple<DateTime, DateTime> GetShiftInterval(this ShiftEvent shiftEvent)
{
return new Tuple<DateTime, DateTime>(ParseDateTime(shiftEvent.StartDate, shiftEvent.StartTime), ParseDateTime(shiftEvent.EndDate, shiftEvent.EndTime));
}
private static DateTime ParseDateTime(string date, string time)
{
string text = string.Concat(date, " ", time);
DateTime result;
if (DateTime.TryParse(text, out result))
{
return result;
}
throw new ArgumentException("Invalid DateTime", text);
}
public static Shift StartShift(this ShiftEvent shiftEvent, Tuple<DateTime, DateTime> interval)
{
return new Shift
{
EmployeeId = shiftEvent.EmpId,
StartOfShiftDate = interval.Item1,
EndOfShiftDate = interval.Item2,
ShiftEvents = new List<ShiftEvent> { shiftEvent }
};
}
}
I ignored StartOfShitTime
& EndOfShiftTime
properties in Shift
class because StartOfShiftDate
& EndOfShiftDate
is enough to store time info.
This solution also assumes that there is no overlap in shift event times.
Upvotes: 2