使用 Linq 查找一组常见对象

Using Linq to find a group of common objects

我想做的是,如果您查看下面的代码,请编写一个 Linq 语句,该语句将 return ShiftEvent 组。一个组由 1.) EmpId, 2.) EndDate/Time of one ShiftEvent = StartDate/Time of one ShiftEvent

然后我想创建一个 Shift 对象,我将 ShiftEvent 组放在 Shift.List 中。

我写了一个半吊子的 Linq 语句,它似乎确实加入了 ShiftEvents..这是我完全迷失的分组部分。

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


}

任何指点都很棒!

您可以先按员工对轮班事件进行分组:

var employeeShifts = _shiftEvents.GroupBy(item => item.EmpId);

然后迭代它们以创建班次。使用第一个班次事件创建第一个班次。如果下一个班次事件在上一个班次结束时开始,则更新班次的结束日期。如果有差距,将班次添加到列表中并从当前班次开始新的班次。

这是完整的解决方案。它有点乱,因为你将时间信息保存为字符串。

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

我不得不使用的扩展方法:

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

我忽略了 Shift 和 class 中的 StartOfShitTime & EndOfShiftTime 属性,因为 StartOfShiftDate & EndOfShiftDate 足以存储时间信息。

此解决方案还假设轮班事件时间没有重叠。