Algorithm for finding overlapping events/times

后端 未结 3 1724
不思量自难忘°
不思量自难忘° 2021-02-04 18:24

While working on custom calendar, I can\'t figure out how to find time slots that overlaps any other time slot.

Time slots start from 0 to 720 (9am to 9pm with each

相关标签:
3条回答
  • 2021-02-04 19:11

    Here's code that will do what you want. As others have mentioned you'd probably be better served by storing date objects, but that's a different issue.

    function getOverlaps(events) {
        // sort events
        events.sort(function (a, b) {
            return a.start - b.start;
        });
    
        var results = [];
        for (var i = 0, l = events.length; i < l; i++) {
            var oEvent = events[i];
            var nOverlaps = 0;
            for (var j = 0; j < l; j++) {
                var oCompareEvent = events[j];
                if (oCompareEvent.start <= oEvent.end && oCompareEvent.end > oEvent.start || oCompareEvent.end <= oEvent.start && oCompareEvent.start > oEvent.end) {
                    nOverlaps++;
                }
            }
            if (nOverlaps > 1) {
                results.push({
                    id: oEvent.id,
                    eventCount: nOverlaps,
                    toString: function () {
                        return "[id:" + this.id + ", events:" + this.eventCount + "]"
                    }
                });
            }
    
        }
        return results;
    }
    
    0 讨论(0)
  • 2021-02-04 19:17

    To me it is easier to use timestamps for each start and end event, that way you can work with them directly or change them to date objects. To get the value, create a date object for each start and end, then:

    var a.start = startDate.getTime();
    var a.end = endDate.getTime();
    

    For overlap:

    if (a.start <= b.start && a.end > b.start ||
        a.start < b.end && a.end >= b.end) {
        // a overlaps b
    }
    

    You can leave them as date objects if you like, the above will work just as well.

    Edit

    Ok here's a working example:

    Assuming a nominal date of 2012-05-15, then the events array looks like:

    // Use iso8601 like datestring to make a local date object
    function getDateObj(s) {
      var bits = s.split(/[- :]/);
      var date = new Date(bits[0], bits[1] - 1, bits[2]);
      date.setHours(bits[3], bits[4], 0);
      return date;
    }
    
    var events = [
      {id: 1, start: getDateObj('2012-05-15 09:00'), end: getDateObj('2012-05-15 09:30')}, 
      {id: 2, start: getDateObj('2012-05-15 09:30'), end: getDateObj('2012-05-15 11:30')}, 
      {id: 3, start: getDateObj('2012-05-15 09:20'), end: getDateObj('2012-05-15 12:00')}, 
      {id: 4, start: getDateObj('2012-05-15 12:20'), end: getDateObj('2012-05-15 12:30')}, 
      {id: 5, start: getDateObj('2012-05-15 18:00'), end: getDateObj('2012-05-15 19:00')}, 
      {id: 6, start: getDateObj('2012-05-15 18:20'), end: getDateObj('2012-05-15 19:20')}
    ];
    
    function getOverlappingEvents(eventArray) {
      var result = [];
      var a, b;
    
      // Sort the event array on start time
      eventArray.sort(function(a, b) {
          return a.start - b.start;
        });
    
      // Get overlapping events
      for (var i=0, iLen=eventArray.length - 1; i<iLen; i++) {
        a = eventArray[i];
        b = eventArray[i + 1];
    
        if ((a.start <= b.start && a.end > b.start) ||
            (a.start < b.end && a.end >= b.end) ) {
           result.push([a.id, b.id]);
        }
      }
      return result;
    }
    
    // Run it    
    alert(getOverlappingEvents(events).join('\n')); // 1,3 2,3 5,6
    
    0 讨论(0)
  • 2021-02-04 19:23

    from my jquery-week-calendar commit, this is how i do it:

        _groupOverlappingEventElements: function($weekDay) {
            var $events = $weekDay.find('.wc-cal-event:visible');
            var complexEvents = jQuery.map($events, function (element, index) {
                var $event = $(element);
                var position = $event.position();
                var height = $event.height();
                var calEvent = $event.data('calEvent');
                var complexEvent = {
                    'event': $event,
                    'calEvent': calEvent,
                    'top': position.top,
                    'bottom': position.top + height
                };
                return complexEvent;
            }).sort(function (a, b) {
                var result = a.top - b.top;
                if (result) {
                    return result;
                }
                return a.bottom - b.bottom;
            });
            var groups = new Array();
            var currentGroup;
            var lastBottom = -1;
            jQuery.each(complexEvents, function (index, element) {
                var complexEvent = element;
                var $event = complexEvent.event;
                var top = complexEvent.top;
                var bottom = complexEvent.bottom;
                if (!currentGroup || lastBottom < top) {
                    currentGroup = new Array();
                    groups.push(currentGroup);
                }
                currentGroup.push($event);
                lastBottom = Math.max(lastBottom, bottom);
            });
            return groups;
        }
    

    there's a bit of component-specific noise around, but you'll get the logic:

    • sort the events by their starting ascending
    • sort the events by their ending ascending
    • iterate over the sorted events and check the starting/ending of the previous event (done rather by position, than by the event properties themself - just because the design might overlap, but the events not ... eg: making a border 2px, events with not overlapping start/end-times might overlap or "touch")
    • each overlapping-group (currentGroup) is a new array inside the groups-array

    soo ... your code might look sth alike this (btw, no need to work with the real date-instances)

    events.sort(function (a, b) {
        var result = a.start - b.start;
        if (result) {
            return result;
        }
        return a.end - b.end;
    });
    var groups = new Array();
    var currentGroup;
    var lastEnd = -1;
    jQuery.each(events, function (index, element) {
        var event = element;
        var start = event.start;
        var end = event.end;
        if (!currentGroup || lastEnd < start) {
            currentGroup = new Array();
            groups.push(currentGroup);
        }
        currentGroup.push(event);
        lastEnd = Math.max(lastEnd, end);
    });
    return groups;
    

    soo ... you are not willed to push some own energy into your problem ... well

    var output = new Array();
    jQuery.each(groups, function (index, element) {
        var group = element;
        if (group.length <= 1) {
            return;
        }
        jQuery.each(group, function (index, element) {
            var event = element;
            var foo = {
                'id': event.id,
                'eventCount': group.length
            };
            output.push(foo);
        });
    });
    
    0 讨论(0)
提交回复
热议问题