简体   繁体   中英

detecting range overlaps in Google Calendar-Style event list

I need help fixing my existing code to accomplish what I am trying to do.

with the following sample data:

var SAMPLE_DATA = [{start: 30, end: 150}, {start: 540, end: 600}, {start: 560, end: 620}, {start: 610, end: 670}];

I need to do the following:

  • iterate through each sample object
  • determine if the current objects range (obj.start:obj.end) overlaps with any other object ranges.
  • record the total number of overlaps for that object into totalSlots property
  • determine the "index" of the object (used for it's left-to-right positioning)

mockup of what I am trying to accomplish:

最终结果的样机

As you can see in the mockup, slotIndex is used to determine the left-to-right ordering of the display. totalSlots is how many objects it shares space with (1 meaning it is the only object). 100 / totalSlots tells me how wide the square can be (ie totalSlots=2 , means it is 100 / 2, or 50% container width).

Current Output from my code

Obj[0] slotIndex=0, totalSlots=0
Obj[1] slotIndex=1, totalSlots=1
Obj[2] slotIndex=1, totalSlots=2
Obj[3] slotIndex=0, totalSlots=1 

expected/desired output from my code:

Obj[0] slotIndex=0, totalSlots=0
Obj[1] slotIndex=0, totalSlots=1
Obj[2] slotIndex=1, totalSlots=2
Obj[3] slotIndex=0, totalSlots=1 

the code:

detectSlots: function(oldEventArr) {
  oldEventArr.sort(this.eventSorter);
  var newEventArr = [],
    n = oldEventArr.length;

  for (var i = 0; i < n; i++) {
    var currObj = oldEventArr[i];
    if ('undefined' == typeof currObj.totalSlots) {
      currObj.slotIndex = 0;
      currObj.totalSlots = 0;
    }
    for (var x = 0; x < n; x++) {
      if (i == x) { 
        continue;
      }
      var nextObj = oldEventArr[x];
      if (currObj.start <= nextObj.end && nextObj.start <= currObj.end) {
        currObj.totalSlots++;
        nextObj.slotIndex++;

      }
    }
    newEventArr.push(currObj);
  }
  return newEventArr;
}

Please help me figure out what is going wrong in my code. I'm about 90% sure the problem lies in the if(currObj.start <= nextObj.end && nextObj.start <= currObj.end) statement where I am assigning/incrementing the values but I could use an extra set of eyes on this.

like this

var not_overlap = (currObj.end <= nextObj.start || nextObj.end <= currObj.start);
if (!not_overlap) { ...

or

var overlap = (currObj.end > nextObj.start && nextObj.end < currObj.start);
if (overlap) { ...

The slotIndex value can be calculated by using graph colouring algorithm. Note that brute force algorithm is exponential in time and will only be a viable solution for a small set of overlapping slots. Other algorithms are heuristics and you won't be guaranteed the least slot possible.

Here is an example of heuristic for your problem:

...
// init
  var newEventArr = [], n = oldEventArr.length;

  for (var i = 0; i < n; i+=1) {
    var currObj = oldEventArr[i];
    newEventArr.push({"start":currObj.start,"end":currObj.end,"slotIndex":undefined,"totalSlots":0});
  }

  var link = {};

  // create link lists and totals
  for (var i = 0; i < n; i+=1) {
    var currObj = newEventArr[i];
    if (!link.hasOwnProperty(""+i))
      link[""+i] = {};

    for (var j = i+1; j < n; j+=1) {
      var nextObj = newEventArr[j];
      var not_overlap = (currObj.end <= nextObj.start || nextObj.end <= currObj.start);
      if (!not_overlap) {        
        currObj.totalSlots+=1;
        nextObj.totalSlots+=1;

        link[""+i][""+j] = 1;
        if (!link.hasOwnProperty(""+j))
          link[""+j] = {};
        link[""+j][""+i] = 1;
      }
    }
  }

  var arrities = [];
  for (var i = 0; i < n; i+=1) {
    arrities.push( {"arrity":newEventArr[i].totalSlots, "indx":i} );
  }

  // sort by arrities [a better solution is using a priority queue]
  for (var i = 0; i < n-1; i+=1) {
    var current_arrity = -1, indx = -1;
    for (var j = i; j < n; j+=1) {
      if (arrities[j].arrity > current_arrity) {
        indx = j;
        current_arrity = arrities[j].arrity;
      }
    }
    var temp = arrities[i];
    arrities[i] = arrities[indx];
    arrities[indx] = temp;
  }

  for (var i = 0; i < n; i+=1) {
    var nodeIndex = arrities[i].indx;
    // init used colors
    var colors = [];
    for (var j = 0; j < n; j+=1) {
      colors.push(0);
    }
    //find used colors on links
    for (var k in link[""+nodeIndex]) {
      var color = newEventArr[k].slotIndex;
      if (color || color === 0)
        colors[color] += 1;
    }
    //find the first unused color
    for (var j = 0; j < n; j+=1) {
      if (colors[j] <= 0) {
        // color the node
        newEventArr[nodeIndex].slotIndex = j;
        break;
      }
    }
  }
  return newEventArr;
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM