从每个用户提供的时间列表中查找所有常用时间

Cyb*_*cop 8 c# linq algorithm

我以前曾问过这个问题.这个想法是一样的,除了我必须找到确定的所有共同时间TimeSpan.

背景

让我们假设,

我想见到一些人,我说我希望在Datetime X(2014-02-16 09:00:00.000)到DateTime Y(2014-02-26 05:00:00.000)之间遇到某些人.而且我说我希望会议能够持续至少N几个小时.

然后我想见的人将回复说我将在以下日期提供:

Date1(Certain date from certain start time to certain end time), 

Date2(certain date from certain time to certain time),

...
Run Code Online (Sandbox Code Playgroud)

等等.

目的

然后我必须找出是否存在包含在所有用户响应中的时间范围.

让我们考虑这些是回应

Attendee1(一些GuidId):

Response1:Start Time = 2014-02-23 09:00 AM,EndTime = 2014-02-23 11:00 AM,

Response2:Start Time = 2014-02-24 10:00 AM,EndTime = 2014-02-24 12:00 PM,

Response3:开始时间= 2014-02-25 10:00 AM,EndTime = 2014-02-25 11:00 AM,

Response4:Start Time = 2014-02-23 01:00 PM,EndTime = 2014-02-17 5:00 PM

Attendee2(一些GuidId):

Response1:Start Time = 2014-02-22 09:00 AM,EndTime = 2014-02-22 05:00 PM,

Response2:Start Time = 2014-02-23 09:00 AM,EndTime = 2014-02-23 05:00 PM,

Response3:开始时间= 2014-02-25 09:00 AM,EndTime = 2014-02-25 12:00 PM,

Attendee3(一些GuidId):

Response1:Start Time = 2014-02-22 11:00 AM,EndTime = 2014-02-22 02:00 PM,

Response2:Start Time = 2014-02-23 04:00 PM,EndTime = 2014-02-23 03:00 PM,

Response3:Start Time = 2014-02-23 4:30 PM,EndTime = 2014-02-23 05:30 PM,

Response4:开始时间= 2014-02-24 02:00 AM,EndTime = 2014-02-24 05:00 PM,

Response5:开始时间= 2014-02-25 11:00 AM,EndTime = 2014-02-25 12:00 PM,

所以,如果可能的话,我应该想出一些可以说这里找到匹配的东西,如果没有,那么只需查找所有用户是否存在公共时间.

Time X to Time Y(X和Y之间的差异至少应该是提到的TimeSpan):( Number of Attendee in this match = Ntype = int,1或2或匹配找到的数量)

Time A to Time B : Number of Attendee in this match = N
Run Code Online (Sandbox Code Playgroud)

...

等等

PS:

它是一个MVC 5.1应用程序,数据库是使用代码优先方法创建的.因此在数据库中有一个名为Appointment的表,它存储StartDateTime和EndDateTime

这是我的 DataModels

public class Appointment
{

    [Key]
    public Guid Id { get; set; }

    public virtual ICollection<Attendee> Attendees { get; set; }

    public DateTime StartDateTime { get; set; }
    public DateTime EndDateTime { get; set; }    
    public TimeSpan MinAppointmentDuration { get; set; }  

}
Run Code Online (Sandbox Code Playgroud)

在这些日期之间,出席会议的人(参加者)将给予回复.每个人(谁都会回复),他们的信息都存储在名为Attendees的数据库表中

public class Attendee
{
    public Guid AttendeeId { get; set; }
    public virtual ICollection<Response> Responses { get; set; } 
}
Run Code Online (Sandbox Code Playgroud)

对于每个用户,他们的响应存储在响应表中,其模型看起来像

public class Response
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public Guid AttendeeId { get; set; }
    public DateTime StartDateTime { get; set; }
    public DateTime EndDateTime { get; set; }

}
Run Code Online (Sandbox Code Playgroud)

这就是我所做的,但它不起作用.

public class CommonTime
{
    public DateTime Start { get; set; }
    public DateTime End { get; set; }
    public TimeSpan MinAppointmenttime { get; set; }
    public int NumAttendees 
    { 
        get { return Responses.Select(x => x.AttendeeId).Distinct().Count(); } 
    }
    public List<DataModels.Response> Responses { get; set; }

    public CommonTime(DataModels.Response response, TimeSpan time)
    {
        Responses = new List<DataModels.Response>();
        Start = response.StartDateTime;
        End = response.EndDateTime;
        MinAppointmenttime = time;
    }

    public void MergeCommonTime(DataModels.Response response)
    {
        if(Start <= response.StartDateTime && response.EndDateTime<=End)
        {
            Start = response.StartDateTime;
            End = response.EndDateTime;
            if((End-Start)>=MinAppointmenttime)
            {
                Responses.Add(response);
            }

        }
    }

    public List<CommonTime> FindCommonMatches(Guid appointmentId)
    {
        var appointment = _db.Appointments.Find(appointmentId);
        var attendees = appointment.Attendees.ToList();
        var matches = new List<CommonTime>();
        bool isFirstAttendee = true;
        foreach (var attendee in attendees)
        {
            if (isFirstAttendee)
            {
                foreach (var response in attendee.Responses)
                {
                    matches.Add(new CommonTime(response, appointment.MinAppointmentDuration));

                }
                isFirstAttendee = false;
            }
            else
            {
                foreach (var response in attendee.Responses)
                {
                    matches.ForEach(x => x.MergeCommonTime(response));
                }
            }
        }

        return matches;
    }

}
Run Code Online (Sandbox Code Playgroud)

所以在这种情况下,如果Attendee X(有一些Guid Id)给出他/她的可用性 在此输入图像描述

Attendee Y给予他/她的可用性

在此输入图像描述

这是我得到的常见匹配

在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述

根据我的解释,这不是我想要的.

那么,我该怎么做才能得到我想要的东西.


编辑:

根据Zache的回答

    public class Meeting
    {
        public DateTime Start { get; set; }
        public DateTime End { get; set; }

        public List<DataModels.Attendee> Attendees { get; set; }

    }

    public class Requirement
    {
        public DateTime Start { get; set; }
        public DateTime End { get; set; }

        public TimeSpan MinHours { get; set; }

        public int MinAttendees { get; set; }

        public IEnumerable<Meeting> Meetings()
        {
            var possibleMeetings = new List<Meeting>();
            var availableHours = (End - Start).TotalHours;

            for (var i = 0; i < availableHours - MinHours.Hours; i++)
                yield return new Meeting
                {
                    Start = Start.AddHours(i),
                    End = Start.AddHours(i+MinHours.Hours)
                };
        }
    }

    public class Scheduler
    {
        public IEnumerable<Meeting> Schedule(Requirement req, List<DataModels.Attendee> attendees)
        {
            var fullMatches = new List<Meeting>();
            var partialMatches = new List<Meeting>();

            foreach (var m in req.Meetings())
            {
                foreach (var a in attendees)
                {
                    if (fullMatches.Any())
                    {
                        if (a.Responses.Any(r => r.StartDateTime <= m.Start && r.EndDateTime >= m.End))
                        {
                            if (m.Attendees == null)
                            {
                                m.Attendees = new List<DataModels.Attendee> { a };
                            }
                            else
                            {
                                m.Attendees.Add(a);
                            }
                        }
                        else
                        {
                            break; // If we found one full match we aren't interested in the partials anymore.
                        }
                    }
                    else
                    {
                        if (a.Responses.Any(r => r.StartDateTime <= m.Start && r.EndDateTime >= m.End))
                        {
                            if (m.Attendees == null)
                            {
                                m.Attendees = new List<DataModels.Attendee> { a };
                            }
                            else
                            {
                                m.Attendees.Add(a);
                            }
                        }
                    }
                }

                if (m.Attendees != null)
                {
                    if (m.Attendees.Count == attendees.Count)
                        fullMatches.Add(m);
                    else if (m.Attendees.Count >= req.MinAttendees)
                        partialMatches.Add(m);
                }
            }

            return fullMatches.Any() ? fullMatches : partialMatches;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

存储库中

public IEnumerable<Meeting> FindCommonMatches(Guid appointmentId)
{
    var appointment = _db.Appointments.Find(appointmentId);
    var attendees = appointment.Attendees.Where(a => a.HasResponded == true).ToList();
    var req = new Requirement
    {
        Start = appointment.StartDateTime,
        End = appointment.EndDateTime,
        MinHours = appointment.MinAppointmentDuration,
        MinAttendees = 1
    };
    var schedule = new Scheduler();
    var schedules = schedule.Schedule(req, attendees);
    return schedules;
 }
Run Code Online (Sandbox Code Playgroud)

StartDate:2/24/2014 11:30:00 AM//我在创建约会时设置的

EndDate:2/25/2014 11:30:00 AM

当第一个用户响应以下数据时:

在此输入图像描述

匹配结果:

在此输入图像描述

在此输入图像描述

当第二位与会者回复以下数据时

在此输入图像描述

匹配结果

在此输入图像描述

在此输入图像描述

这里常见的匹配应该是:

Match1: 2/24/2014 10:00:00 AM   to   2/24/2014 11:00:00 AM
 Match2: 2/25/2014 9:00:00 AM   to   2/25/2014 11:00:00 AM
Run Code Online (Sandbox Code Playgroud)

sto*_*roz 6

以下GetMeetingWindows函数将返回所有匹配窗口和可用与会者的列表.然后可以根据需要对结果应用完整或最小的参与者规定,例如

GetMeetingWindows(attendees, TimeSpan.FromMinutes(60)).Where(x => x.AvailableAttendees.Count() == attendees.Count());
Run Code Online (Sandbox Code Playgroud)

我假设所提供的与会者回复已经考虑了会议可用的总时间范围,但如果不是这种情况,则可以将此限制作为额外的与会者添加,然后在结果中对其进行过滤,例如

GetMeetingWindows(...).Where(x => x.AvailableAttendees.Contains(meetingRoom));
Run Code Online (Sandbox Code Playgroud)

代码:

public class Attendee
{
    public ICollection<Response> Responses { get; set; } 
}

public class Response
{
    public DateTime StartDateTime { get; set; }
    public DateTime EndDateTime { get; set; }
}

public class Window
{
    public DateTime StartDateTime { get; set; }
    public DateTime EndDateTime { get; set; }
    public IEnumerable<Attendee> AvailableAttendees { get; set; }   
}

public IEnumerable<Window> GetMeetingWindows(IEnumerable<Attendee> attendees, TimeSpan meetingDuration)
{
    var windows = new List<Window>();
    var responses = attendees.SelectMany(x => x.Responses).Where(x => x.EndDateTime - x.StartDateTime >= meetingDuration);

    foreach(var time in (responses.Select(x => x.StartDateTime)).Distinct())
    {
        var matches = attendees.Select(x => new { 
            Attendee = x, 
            MatchingAvailabilities = x.Responses.Where(y => y.StartDateTime <= time && y.EndDateTime >= time.Add(meetingDuration)) 
        });

        windows.Add(new Window { 
            StartDateTime = time, 
            EndDateTime = matches.SelectMany(x => x.MatchingAvailabilities).Min(x => x.EndDateTime), 
            AvailableAttendees = matches.Where(y => y.MatchingAvailabilities.Any()).Select(x => x.Attendee) 
        });
    }

    foreach(var time in (responses.Select(x => x.EndDateTime)).Distinct())
    {
        var matches = attendees.Select(x => new { 
            Attendee = x, 
            MatchingAvailabilities = x.Responses.Where(y => y.EndDateTime >= time && y.StartDateTime <= time.Add(-meetingDuration)) 
        });

        windows.Add(new Window { 
            EndDateTime = time, 
            StartDateTime = matches.SelectMany(x => x.MatchingAvailabilities).Max(x => x.StartDateTime), 
            AvailableAttendees = matches.Where(y => y.MatchingAvailabilities.Any()).Select(x => x.Attendee) 
        });
    }

    return windows.GroupBy(x => new { x.StartDateTime, x.EndDateTime }).Select(x => x.First()).OrderBy(x => x.StartDateTime).ThenBy(x => x.EndDateTime);
}

public void Test() 
{
    var attendees = new List<Attendee>();
    attendees.Add(new Attendee { Responses = new[] { 
        new Response { StartDateTime = DateTime.Parse("2014-02-24 9:00:00 AM"), EndDateTime = DateTime.Parse("2014-02-24 11:00:00 AM") },
        new Response { StartDateTime = DateTime.Parse("2014-02-24 2:00:00 PM"), EndDateTime = DateTime.Parse("2014-02-24 4:00:00 PM") },
        new Response { StartDateTime = DateTime.Parse("2014-02-25 9:00:00 AM"), EndDateTime = DateTime.Parse("2014-02-25 11:00:00 AM") },
        new Response { StartDateTime = DateTime.Parse("2014-02-25 3:00:00 PM"), EndDateTime = DateTime.Parse("2014-02-25 5:00:00 PM") }
    }});
    attendees.Add(new Attendee { Responses = new[] { 
        new Response { StartDateTime = DateTime.Parse("2014-02-24 10:00:00 AM"), EndDateTime = DateTime.Parse("2014-02-24 11:00:00 AM") },
        new Response { StartDateTime = DateTime.Parse("2014-02-24 4:00:00 PM"), EndDateTime = DateTime.Parse("2014-02-24 5:00:00 PM") },
        new Response { StartDateTime = DateTime.Parse("2014-02-25 9:00:00 AM"), EndDateTime = DateTime.Parse("2014-02-25 11:00:00 AM") }
    }});

    var windows = GetMeetingWindows(attendees, TimeSpan.FromMinutes(60));
    foreach(var window in windows)
    {
        Console.WriteLine(String.Format("Start: {0:yyyy-MM-dd HH:mm}, End: {1:yyyy-MM-dd HH:mm}, AttendeeCount: {2}", window.StartDateTime, window.EndDateTime, window.AvailableAttendees.Count()));
    }
}
Run Code Online (Sandbox Code Playgroud)