我以前曾问过这个问题.这个想法是一样的,除了我必须找到确定的所有共同时间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)
以下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)