2

I want to find common DateTime from available list of DateTime in my database.

Background

Lets suppose,

I want to meet some peoples, and I say I want to meet certain peoples between Datetime X(2014-02-16 09:00:00.000) to DateTime Y(2014-02-26 05:00:00.000).

Then the peoples i want to meet with will reply saying i will be available in following dates: Date1(Certain date from certain start time to certain end time), Date2(certain date from certain time to certain time),...and so on.

Lets consider these are the responses

Attendee1(Some GuidId):

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

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

Response3 : Start Time=2014-02-25 10:00 AM, EndTime = 2014-02-17 11:00 AM,

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

Attendee2(Some GuidId):

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

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

Response3 : Start Time=2014-02-25 09:00 AM, EndTime = 2014-02-17 12:00 PM,

Attendee3(Some GuidId):

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

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

Response3 : Start Time=2014-02-23 04:00 PM, EndTime = 2014-02-17 05:00 PM,

Response4 : Start Time=2014-02-24 02:00 AM, EndTime = 2014-02-17 05:00 PM,

Response5 : Start Time=2014-02-25 11:00 AM, EndTime = 2014-02-17 12:00 PM,

Here, in above scenario, system should provide matching dates as:

2014-02-23 04:00 PM to 2014-02-23 05:00 PM 

and

2014-02-25 11:00 AM to 2014-02-25 12:00 PM

So once every one has replied. I would like to find the common DateTime or common List<DateTime>(if exists).

Or find the DateTime(List) that has highest number of matches.

I know I have to show what I have tried and what I have done, but I have no idea in my mind how should I start.

Any kind of suggestions or hints would be appreciated.

Edit 1:

So in database there is table called Appointment which stores the StartDateTime and EndDateTime

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

}

and between these dates people(Attendee) attending will give their response. Each person(who will respond), their information is stored in database table called Attendees

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

And for each user their response is stored in Responses table whose model would look like

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

}

PS: It is a MVC application and database is created using code first approach.

Also, would it make sense to add AppointmentId to Response table? or would that be bad approach? But I think it would make the querying easier.

Cybercop
  • 8,475
  • 21
  • 75
  • 135
  • well the date are stored in database MS-Sql and my application is Asp.Net MVC. – Cybercop Feb 17 '14 at 10:14
  • So you want to find overlapping between Appointments(start and end time ) available and Responses(Attendee's start and end time)? and you looking for List of those overlapping, so that you can use it? – dipak Feb 17 '14 at 10:29
  • @Dipak I don't quite get what you are saying...sorry! but the idea is each Attendee will provide list of start datetime and endtime and I want to find common date and time(one or many that matches the responses from attendees). Do you get what I mean? – Cybercop Feb 17 '14 at 10:38
  • common means Overlap...ISn't it? – dipak Feb 17 '14 at 10:41
  • @Dipak you can say that – Cybercop Feb 17 '14 at 10:42
  • infact you need to know who are available for example at (2014-02-17), right ? – Amir Sherafatian Feb 17 '14 at 10:45
  • right now for a start i want to know if there is common time that would match all the attendees(based upon the `List`) they have provided. – Cybercop Feb 17 '14 at 10:47
  • its matter to you that get your query in one database connection ? or can you do that in memory using `linq to object`? (how about: getting all data in one connection first, and then work on it in memory ) – Amir Sherafatian Feb 17 '14 at 10:50
  • @am1r_5h I can get all data in one connection and work it in memory, wouldn't be a problem – Cybercop Feb 17 '14 at 10:53
  • @BipLov, You can refer to the answers I gave while ago here http://stackoverflow.com/questions/21582365/want-to-find-duplicates-in-list-which-have-overlaps-timespans/21583537#comment32632852_21583537 – dipak Feb 17 '14 at 11:15

1 Answers1

1

An approach that might work:

  • Calculate a List of the combinations that everyone can attend.
  • You would start with all responses for the first Attendee, and then enumerate through the other attendees.
  • As you enumerate through, for each potential time that can still work, narrow the time window to fit all of the times that all possible attendees can use

Might need some touching up to handle edge cases, but the following code should get you on the right path:

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

  public MeetingTime(Response response) 
  {
    Start = response.StartDateTime;
    End = response.EndDateTime;
    Responses = new List<Response>();
  }

  public void MergeMeetingTime(Response response) 
  {
    // if response times are within current times, can be merged
    if (Start <= response.StartDateTime && response.EndDateTime <= End)
    {
       Start = response.StartDateTime;
       End = response.EndDateTime;
       Responses.Add(response);
    }
  }
}

public List<MeetingTime> FindMeetingTimes() 
{
  var attendees = GetAttendees();
  var times = new List<MeetingTime> 
  bool isFirstAttendee = true;
  foreach (Attendee attendee in attendees) 
  {
    if (isFirstAttendee) 
    {
      foreach (Response response in attendee.Responses)
      {
        times.Add(new MeetingTime(response));
      }
      isFirstAttendee = false;
    } 
    else 
    {
      // Go through attendee.Responses and compare each with the current times
      // for each one, if it overlaps with a MeetingTime, then adjust 
      // the Start/End dates accordingly and increment Count
      // Uses same approach as above.          
      foreach (Response response in attendee.Responses)
      {
        times.ForEach(x => x.MergeMeetingTime(response));
      }
    }
  }
  // Remove all times where not everyone can attend
  times.Remove(x => x.NumAttendees < attendees.Count()).ToList();
  return times;
}

At the end, you remove all times where not everyone can attend. The times that are remaining should each have Start/End DateTimes that all Attendees can make.

Yaakov Ellis
  • 40,752
  • 27
  • 129
  • 174
  • `DateTime.MaxValue`? `DateTime.MinValue`? – Cybercop Feb 17 '14 at 10:28
  • Just switched it to use `DateTime?` vars for tracking. – Yaakov Ellis Feb 17 '14 at 10:33
  • for the else part, i didn't quite understand what you are trying to mean. – Cybercop Feb 17 '14 at 12:46
  • In your `times.Remove` part at the end, i don't think `x=>x.NumAttendees` is valid – Cybercop Feb 17 '14 at 13:37
  • Previously in the code, times would only be merged if there was any possible overlap. So you could have a time in there that never overlapped with any other Attendee Response. Thus it would still be in `times` but it wouldnt be a good result since it would only contain a time that worked for one Attendee. – Yaakov Ellis Feb 17 '14 at 13:40
  • Also - your question was asking for an approach to solving your problem. I think that a valid approach has been provided - you should still do testing and make sure that it covers all scenarios and edge cases. – Yaakov Ellis Feb 17 '14 at 13:41
  • I have some questions, i'll update my question then may be you can help. One question is while you are defining `NumAttendees` how is the value of this field being set? and what is `Distinct` doing? – Cybercop Feb 17 '14 at 14:38
  • `NumAttendees` gets the number of distinct `AttendeeId` values in a `MeetingTime.Responses` collection (need distinct because you could have multiple matching Responses from one attendee, an edge case that could use some more attention). – Yaakov Ellis Feb 17 '14 at 14:40
  • now I have everything you mentioned except folowing line `times.Remove(x => x.NumAttendees – Cybercop Feb 17 '14 at 14:48
  • sorry to interrupt again but, in `times` now represents `List();` and in the line `times.Remove(x => x.NumAttendees < attendees.Count()).ToList();` `x=>x.NumAttendees` wouldn't be possible because each `MeetingTimes` will have a NumAttendees, i think we will have to find `NumAttendees` with highest value? – Cybercop Feb 18 '14 at 08:05
  • The point was that you only want to keep `MeetingTime` items in the `times` collection if it has all of the attendees able to make that time. – Yaakov Ellis Feb 18 '14 at 08:07
  • but now it contains all the matching times, even if it doesn't match all the users :( – Cybercop Feb 18 '14 at 09:38