首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >(Js)调度冲突检测算法逻辑长度

(Js)调度冲突检测算法逻辑长度
EN

Code Review用户
提问于 2020-04-05 23:18:21
回答 1查看 661关注 0票数 3
代码语言:javascript
复制
// TODO: https://codereview.stackexchange.com/questions/240011/js-schedule-conflict-detection-algorithm-logic-length

// List with handle
Sortable.create(dashboard, {
  handle: '.fa-bars',
  animation: 150
});

// To Instantiate & Control the Modal 
var modal = document.getElementById("modal");
var addSchedule = document.getElementById('add');
var scheduleCount = 0;
var trackedSchedule;

addSchedule.addEventListener('click', function (event) {
  scheduleCount += 1;
  // console.log(event.target.id); // Anomoly Here
  trackedSchedule = `schedule-${scheduleCount}`;
  console.log(trackedSchedule);
  // Change modal to load with default values (prefill name with scheduleCount)
  // Change Text to Read "Create Schedule" instead of "Edit Schedule"
  modal.style.display = "block";  
});


var span = document.getElementsByClassName("close")[0];
span.onclick = function() {
  modal.style.display = "none";
  scheduleCount -= 1;
}

window.onclick = function(event) {
  if (event.target == modal) {
    modal.style.display = "none";
    scheduleCount -= 1;
  }
}

function getDate() {
  var today = new Date();
  var dd = String(today.getDate()).padStart(2, '0');
  var mm = String(today.getMonth() + 1).padStart(2, '0');
  var yyyy = today.getFullYear();
  today = `${yyyy}-${mm}-${dd}`;
  return today;
}

var savedSchedules = {};
var data = [];

var layout = {
  showlegend: false,
  xaxis: {range: ['2020-01-01 00:00:00', '2020-01-01 23:59:59'],
          showgrid: false,
          zeroline: false,
          showline: true,
          tickformat: '%H:%M:%S'
         },
  yaxis: {rangemode: 'tozero',
          range: [-0.75, 6.5],
          showline: true,
          zeroline: false,
          tickvals: [0, 1, 2, 3, 4, 5, 6],
          ticktext: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
         }
};

// To Save the Schedule Data from the Modal
var scheduleName = document.getElementById('schedule-name');
var warningMsg = document.getElementById('warning');
var saveButton = document.getElementById('submit-schedule');
var overwriteButton = document.getElementById('overwrite-schedule');
var sunday = document.getElementById('Sunday');
var monday = document.getElementById('Monday');
var tuesday = document.getElementById('Tuesday');
var wednesday = document.getElementById('Wednesday');
var thursday = document.getElementById('Thursday');
var friday = document.getElementById('Friday');
var saturday = document.getElementById('Saturday');
var daysOfWeek = {'Sunday': sunday,
                  'Monday': monday,
                  'Tuesday': tuesday,
                  'Wednesday': wednesday,
                  'Thursday': thursday,
                  'Friday': friday,
                  'Saturday': saturday}
var startTime;
var endTime;

document.getElementById('startTime').addEventListener("input", function (event) {
      startTime = event.target.value;
});

document.getElementById('endTime').addEventListener("input", function (event) {
      endTime = event.target.value;
});


function editSchedule(event) {
  console.log(event.target.id);
  // Init Modal from saved dict
  schedule = savedSchedules[event.target.id];
  Object.entries(schedule).forEach(function([key, value]) {
    if (value) {
      if (key != 'Name') {
         var day = document.getElementById(key);
         day.checked = true;
      }
      else {
        scheduleName.value = value;
      }
      var startTime_ = document.getElementById('startTime');
      var endTime_ = document.getElementById('endTime');
      startTime_.value = value.StartTime;
      endTime_.value = value.EndTime;
    }
  });
  modal.style.display = 'block';
};

// Need to validate logical comparators for provided time format?
function checkForConflict(c1s, c1e, c2s, c2e) { // case 1 start time, case 1 end time, case 2 start time, case 2 end time
  // if (c1 intersects c2) or (c2 intersects c1) or (c2 contained in c1) or (c1 contained in c2) or (anything else?)
  if ( (c1s >= c2s && c1e >= c2s) || (c1s <= c2s && c1e <= c2e) || (c1s <= c2s && c1e >= c2e) || (c1s >= c2s && c1e <= c2e) ) {
    return true;
  } else {return false};
}

saveButton.onclick = function() {
  // Disable save if not all reqd fields filled -> https://stackoverflow.com/questions/39880389/disable-button-until-fields-are-full-pure-js
  // while (!scheduleName || !startTime || !endTime || !(sunday.checked&&monday.checked&&tuesday.checked&&wednesday.checked&&thursday.checked&&friday.checked&&saturday.checked)) {
  //     saveButton.disabled = true;
  //   } 
  
  // Check that fields are filled
  if (!scheduleName || !startTime || !endTime || !sunday.checked&&!monday.checked&&!tuesday.checked&&!wednesday.checked&&!thursday.checked&&!friday.checked&&!saturday.checked) {
    warningMsg.textContent = "All required fields must contain data";
  } 
  
  // Try to Save the Schedule
  else {           
    // Check for Schedule Conflicts
    var conflictData;
    if (savedSchedules && Object.keys(savedSchedules).length >= 1) {
        console.log("At least one schedule detected");
        // Loop through saved schedules (key = 'schedule-x')
        Object.entries(savedSchedules).forEach(function([key, value]) {
          schedule_ = savedSchedules[key];
          // Loop through the schedule (key_ = 'Name', 'Sunday', ...)
          Object.entries(schedule_).forEach(function([key_, value_]) {
            // Only check if weekday has a time value and weekdays that are selected
            if (key_ != 'Name' && value_ && daysOfWeek[key_].checked) { 
                // if there is conflict -> store to variable
                var conflict = checkForConflict(value_.StartTime, value_.EndTime, startTime, endTime);
                if (conflict) {
                  console.log('Conflict Detected');
                  conflictData = [key_, `Start: ${value_.StartTime}/${startTime}`, `End: ${value_.EndTime}/${endTime}`];
                }
            }
          });
        });
    }
    // Alert User of Conflict
    if (conflictData) {
      console.log(conflictData);
      warningMsg.textContent = `There is a scheduling conflict ${conflictData}`;
      overwriteButton.style.display = 'block';
      // Handle Conflict Resolution -> Overwrite Schedule, etc
    } 
    
    // Save the Schedule
    else {
      var dict = {
        Name: scheduleName.value,
        Sunday: sunday.checked && startTime && endTime ? {StartTime: startTime, EndTime: endTime} : null,  
        Monday: monday.checked && startTime && endTime ? {StartTime: startTime, EndTime: endTime} : null,
        Tuesday: tuesday.checked && startTime && endTime ? {StartTime: startTime, EndTime: endTime} : null,
        Wednesday: wednesday.checked && startTime && endTime ? {StartTime: startTime, EndTime: endTime} : null,
        Thursday: thursday.checked && startTime && endTime ? {StartTime: startTime, EndTime: endTime} : null,
        Friday: friday.checked && startTime && endTime ? {StartTime: startTime, EndTime: endTime} : null,
        Saturday: saturday.checked && startTime && endTime ? {StartTime: startTime, EndTime: endTime} : null,
      }

      savedSchedules[trackedSchedule] = dict;
      console.log(savedSchedules);

      // Add to the graph
      var randomColor = Math.floor(Math.random()*16777215).toString(16); // Make color indicative of temperature
      Object.entries(dict).forEach(function([key, value]) {
        if (key != 'Name' && value) {

          data.push({
            x: [`${getDate()} ${value.StartTime}`, `${getDate()} ${value.EndTime}`], 
            y: [key, key], 
            hovertext: dict.Name,
            type: 'scatter',
            mode: 'markers',
            marker: {
              size: 30,
              color: [randomColor, randomColor],
            }
          });

          data.push({
            x: [`${getDate()} ${value.StartTime}`, `${getDate()} ${value.EndTime}`], 
            y: [key, key], 
            hovertext: scheduleName.value,
            type: 'scatter',
            mode: "lines",
            line: {
              width: 30,
              color: randomColor,
            },
          });

        };
      });

      Plotly.newPlot('graph', data, layout);


      // Create the Dashboard Entry
      var newLine = document.createElement('div');
      newLine.classList.add('list-group-item');
      var newButton = document.createElement('div');
      newButton.classList.add('add-button');
      newButton.id = trackedSchedule;
      newButton.addEventListener('click', editSchedule);    
      var newTemp = document.createElement('a');
      var newName = document.createElement('a');
      newName.textContent = scheduleName.value;
      newName.classList.add('name-font');
      var newWeekSet = document.createElement('div');
      newWeekSet.classList.add('right-align');
      Object.entries(dict).forEach(function([key, value]) {
        if (key != 'Name') {
           var newDay = document.createElement('label')
           // newDay.classList.add('day-font');
           newDay.style.display = 'inline-block';
           newDay.style.margin = '5px';
           newDay.style.fontStyle = 'italic';
           var newDayIcon;
           newDay.textContent = key.charAt(0);
           if (value) {
             newDayIcon = document.createElement('i');
             newDayIcon.classList.add('far', 'fa-check-circle', 'fa-xs');
             // Why doesn't class work for this?
             newDayIcon.style.display = 'block';
             newDayIcon.style.color = 'Blue';
           } 
           else {
             newDayIcon = document.createElement('i');
             newDayIcon.classList.add('far', 'fa-times-circle', 'fa-xs');
             // Why doesn't class work for this?
             newDayIcon.style.display = 'block';
             newDayIcon.style.color = 'Orange';
           }
           newDay.append(newDayIcon);
           newWeekSet.appendChild(newDay);
        };
      });
      var newMoveIcon = document.createElement('i');
      newMoveIcon.classList.add('fas', 'fa-bars');
      newMoveIcon.style.display = 'inline-block';
      newMoveIcon.style.paddingTop = '10px';
      newMoveIcon.style.paddingLeft = '20px'

      newLine.appendChild(newButton);
      newLine.appendChild(newName);
      newLine.appendChild(newWeekSet);
      newWeekSet.appendChild(newMoveIcon);
      dashboard.appendChild(newLine);

      modal.style.display = "none";
    }
  }
}
代码语言:javascript
复制
@import url(https://fonts.googleapis.com/css?family=Open+Sans:700,300);
 body {
	 background: white;
	 font-family: 'Open Sans', Helvetica, sans-serif;
	 -webkit-font-smoothing: antialiased;
	 -moz-osx-font-smoothing: grayscale;
}

.add-button {
  display: inline-block;
  height: 50px;
  width: 50px;
  color: orange;
  padding-left: 18px;
  padding-top: 10px;
  background: white;
  border: 2px solid orange;
  border-radius: 25px;
  cursor: pointer;
}

.name-font {
  font-size: 2em;
  padding-left: 35px;
}

.submit-button {
  display: inline-block;
  border: 1px solid orange;
  border-radius: 5px;
  height: 40px;
  background-color: white;
  cursor: pointer;
}

.submit-button:hover {
  background-color: #ffd796
}

.clickable {
  cursor: pointer;
}

.right-align {
  float: right;
}

/* The Modal (background) */
.modal {
  position: fixed;
  display: none;
  padding-left: 75%;
  left: 0;
  top: 0;
  width: 100%; 
  height: 100%; 
  overflow: auto; 
  background-color: rgba(255,255,255,0.5); 
}

/* Modal Content */
.modal-content {
  padding: 20px;
  border: 2px solid orange;
  height: 100%;
  overflow: auto;
}

/* The Close Button */
.close {
  display: inline;
  color: gray;
  float: right;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}

/* Checkboxes */
.form-group {
  display: block;
  margin-bottom: 5px;
}

.form-group input {
  padding: 0;
  height: initial;
  width: initial;
  margin-bottom: 0;
  display: none;
  cursor: pointer;
}

.form-group label {
  position: relative;
  cursor: pointer;
}

.form-group label:before {
  content:'';
  -webkit-appearance: none;
  background-color: transparent;
  border: 2px solid orange;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), inset 0px -15px 10px -12px rgba(0, 0, 0, 0.05);
  padding: 10px;
  display: inline-block;
  position: relative;
  vertical-align: middle;
  cursor: pointer;
  margin-right: 5px;
}

.form-group input:checked + label:after {
  content: '';
  display: block;
  position: absolute;
  top: 4px;
  left: 9px;
  width: 6px;
  height: 14px;
  border: solid orange;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* Clocklet */
.labels {
  display: block;
}

 .clock {
  display: block;
  z-index: 10000;
}

 /* minute - dial, hand, selected tick, hovered tick */
.clocklet-color-example .clocklet-plate {
  box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.2);
  border: 1px solid gray;
}
.clocklet-color-example[data-clocklet-placement="bottom"] {
  background-color: transparent;
  border: none;
  box-shadow: none;
}
  .clocklet-color-example .clocklet-dial--minute { background-color: orange; color: white;}
  .clocklet-color-example .clocklet-hand--minute { background-color: white; border: 1px solid gray; width: 3px;}
  .clocklet-color-example .clocklet-tick--minute.clocklet-tick--selected { background-color: white; color: orange;}
  .clocklet-color-example.clocklet--hoverable:not(.clocklet--dragging) .clocklet-tick--minute:hover { background-color: white; color: orange;}

  /* hour - dial, hand, selected tick, hovered tick */
  .clocklet-color-example .clocklet-dial--hour { background-color: white; }
  .clocklet-color-example .clocklet-hand--hour { background-color: white; border: 1px solid gray; width: 3px;}
  .clocklet-color-example .clocklet-tick--hour.clocklet-tick--selected { background-color: orange; }
  .clocklet-color-example.clocklet--hoverable:not(.clocklet--dragging) .clocklet-tick--hour:hover { background-color: orange; }

  /* hand origin */
  .clocklet-color-example .clocklet-hand-origin { background-color: gray; }

  /* ampm */
  .clocklet-color-example .clocklet-ampm::before { background-color: orange; }
  .clocklet-color-example .clocklet-ampm:hover::before { background-color: white; color: orange; border: 1px solid orange;}
  .clocklet-color-example .clocklet-ampm[data-clocklet-ampm="pm"]::before { background-color: orange; }
  .clocklet-color-example .clocklet-ampm[data-clocklet-ampm="pm"]:hover::before { background-color: white; }
代码语言:javascript
复制
      Add Schedule
    
  



  
    
      Edit Schedule
      
    
    
    
      
      Start Time
      
      End Time
      
      
      


      
        
        Sunday
      
      
        
        Monday
      
      
        
        Tuesday
      
      
        
        Wednesday
      
      
        
        Thursday
      
      
        
        Friday
      
      
        
        Saturday
      
    
    
    
    
    
    Create Schedule
  Overwrite Schedule

第一篇文章-我希望我理解社会的目的/意图。我正在尝试创建一个使用Js的暖通空调管理WebUI的温度调度器(尽管您不会在代码中找到任何关于设置温度的内容)。代码评审的目的与调度冲突算法&代码相关联。也就是说,在某一周或某一时间(或房间)的某一天,绝对不应出现重复的温度设定。

我希望看看是否有任何改进我的时间表冲突检测算法的建议--我并不完全满意它,因为在我看来,逻辑很难理解。找到代码(很多遗漏,所以如果有什么不清楚的地方让我知道)和CodePen链接(没有遗漏)

代码语言:javascript
复制
var scheduleName = document.getElementById('schedule-name');
var warningMsg = document.getElementById('warning');
var saveButton = document.getElementById('submit-schedule');
var sunday = document.getElementById('Sunday'); // checkbox
var monday = document.getElementById('Monday'); // checkbox
var tuesday = document.getElementById('Tuesday'); // checkbox
var wednesday = document.getElementById('Wednesday'); // checkbox
var thursday = document.getElementById('Thursday'); // checkbox
var friday = document.getElementById('Friday'); // checkbox
var saturday = document.getElementById('Saturday'); // checkbox
var daysOfWeek = {'Sunday': sunday,
                  'Monday': monday,
                  'Tuesday': tuesday,
                  'Wednesday': wednesday,
                  'Thursday': thursday,
                  'Friday': friday,
                  'Saturday': saturday}

function checkForConflict(c1s, c1e, c2s, c2e) { // case 1 start time, case 1 end time, case 2 start time, case 2 end time
  // if (c1 intersects c2) or (c2 intersects c1) or (c2 contained in c1) or (c1 contained in c2) or (anything else?)
  if ( (c1s >= c2s && c1e >= c2s) || (c1s <= c2s && c1e <= c2e) || (c1s <= c2s && c1e >= c2e) || (c1s >= c2s && c1e <= c2e) ) {
    return true;
  } else {return false};
}

saveButton.onclick = function() {

  // Check that fields are filled
  if (!scheduleName || !startTime || !endTime || !sunday.checked&&!monday.checked&&!tuesday.checked&&!wednesday.checked&&!thursday.checked&&!friday.checked&&!saturday.checked) {
    warningMsg.textContent = "All required fields must contain data";
  } 

  // Try to Save the Schedule
  else {           
    // Check for Schedule Conflicts
    var conflictData;
    if (savedSchedules && Object.keys(savedSchedules).length >= 1) {
        console.log("At least one schedule detected");
        // Loop through saved schedules (key = 'schedule-x')
        Object.entries(savedSchedules).forEach(function([key, value]) {
          schedule_ = savedSchedules[key];
          // Loop through the schedule (key_ = 'Name', 'Sunday', ...)
          Object.entries(schedule_).forEach(function([key_, value_]) {
            // Only check if weekday has a time value and weekdays that are selected
            if (key_ != 'Name' && value_ && daysOfWeek[key_].checked) { 
                // if there is conflict -> store to variable
                var conflict = checkForConflict(value_.StartTime, value_.EndTime, startTime, endTime);
                if (conflict) {
                  console.log('Conflict Detected');
                  conflictData = [key_, `Start: ${value_.StartTime}/${startTime}`, `End: ${value_.EndTime}/${endTime}`];
                }
            }
          });
        });
    }
    // Alert User of Conflict
    if (conflictData) {
      console.log(conflictData);
      warningMsg.textContent = `There is a scheduling conflict ${conflictData}:  - Cannot proceed...`;
      // Handle Conflict Resolution -> Overwrite Schedule, etc
    } 

    // Save the Schedule
    else {...}

savedSchedules的格式如下所示:

代码语言:javascript
复制
{
  schedule-1: {
    Name: ,
    Sunday: null,
    Monday: null,
    Tuesday: null,
    Wednesday: null,
    Thursday: null, 
    Friday: null,
    Saturday: {StartTime: 12:55 A.M., EndTime: 1:35 P.M.}
  },
  schedule-2: {...},
  ...
}

对我来说,// Check for Schedule Conflicts块在逻辑和长度上都很简洁。我希望第二组更有经验的眼睛可以确认这是相当合理的逻辑/结构,或者提供一个更好的选择。

链接到CodePen

EN

回答 1

Code Review用户

回答已采纳

发布于 2020-04-06 00:10:49

您对复选框的检索有点冗长。考虑使用单个选择器来获得所有的选择器,而不是单独选择每个选择器。

代码语言:javascript
复制
const daysOfWeek = {};
for (const input of document.querySelectorAll('.form-group > input[type="checkbox"]') {
  daysOfWeek[input.id] = input;
}

然后,与其检查每个单独的变量以查看是否检查了它,不如迭代该对象以查看是否检查了任何值。

为了避免缩进,请考虑在遇到错误时尽早使用return,而不是在没有错误时使用非常大的else块。

您的savedSchedules是一个从未重新分配过的对象:

代码语言:javascript
复制
var savedSchedules = {};

因此,它将永远是真实的- if (savedSchedules &&检查是多余的,因为它永远是真实的。

也不需要事先检查对象有多少个键,只需遍历所有这些键就行了。如果没有,那么就不会发现任何冲突。这样Object.keys(savedSchedules).length >= 1就可以被删除。

Object.entries's回调接受键和值的条目数组参数。由于您已经将该值作为变量,因此不需要通过遍历[key]来再次选择它:

代码语言:javascript
复制
Object.entries(savedSchedules).forEach(function([key, value]) {
  schedule_ = savedSchedules[key];

简化为

代码语言:javascript
复制
Object.entries(savedSchedules).forEach(function([key, schedule_]) {

但是,由于您实际上并没有使用计划名称( key),所以最好删除它,而使用Object.values

代码语言:javascript
复制
Object.values(savedSchedules).forEach(function(schedule_) {

但是,既然你试图找出是否有冲突,当发现问题时,短路就更合适了。考虑使用for循环代替。当发现问题时,告诉用户它和return

变量名后面的下划线令人困惑--它们与我所知道的约定不匹配。看起来,它们是为了区分单个计划的对象和当天的对象值以及当天的对象值。我认为应该在变量名中显式地注意到这一点,例如scheduledaydayObj

而且,由于您使用的是Object.entries,您的环境支持ES6 -在ES6中,您应该在可能的情况下总用const声明变量,而不是var (只有在需要重新分配时才声明let )。

当您深入到验证阶段并与value_.StartTime变量名称进行比较时,D24变量名(来自输入)有点不清楚。也许可以调用输入变量inputStartTime (endTime也是这样)来减少可能的混淆?

冲突检查器是一个不错的代码块,可能应该在它自己的函数中使用。这使您可以从单击监听器中看到完整的单击过程的概要,而不必滚动大量代码才能获得大致的概念。

通常应该避免分配给onclick。如果只执行一次,它就可以工作,但是如果任何其他代码都遵循相同的错误做法,并且试图做同样的事情,您以前的侦听器就会被覆盖。最好始终使用addEventListener

全文:

代码语言:javascript
复制
const daysOfWeek = {};
for (const input of document.querySelectorAll('.form-group > input[type="checkbox"]')) {
    daysOfWeek[input.id] = input;
}
const getConflicts = () => {
    for (const schedule of Object.values(savedSchedules)) {
        // Loop through the schedule (key_ = 'Name', 'Sunday', ...)
        for (const [day, dayObj] of Object.entries(schedule)) {
            // Only check if weekday has a time value and weekdays that are selected
            if (day === 'Name' || !dayObj || !daysOfWeek[day].checked) {
                continue;
            }
            // if there is conflict, return it
            const conflict = checkForConflict(dayObj.StartTime, dayObj.EndTime, inputStartTime, inputEndTime);
            if (conflict) {
                console.log('Conflict Detected');
                const conflictData = [day, `Start: ${dayObj.StartTime}/${inputStartTime}`, `End: ${dayObj.EndTime}/${inputEndTime}`];
                return `There is a scheduling conflict ${conflictData}:  - Cannot proceed...`;
            }
        }
    }
};
saveButton.addEventListener('click', () => {
    // Check that fields are filled
    if (!scheduleName || !inputStartTime || !inputEndTime || Object.values(daysOfWeek).every(input => !input.checked)) {
        warningMsg.textContent = 'All required fields must contain data';
        return;
    }
    // Check for Schedule Conflicts
    const conflictsMessage = getConflicts();
    if (conflictsMessage) {
        // Handle Conflict Resolution -> Overwrite Schedule, etc
        // Probably CALL A FUNCTION HERE, don't write it all inside this click listener
        return;
    }
    // Save the Schedule

    // ...
});

schedule对象的生成没有显示,但是最好不必执行if (day === 'Name'检查,而不是将日程名称与日期键组合在一起,考虑将天放在一个完全独立的属性中,例如:

代码语言:javascript
复制
{
  scheduleName: 'schedule-1',
  days: {
    Sunday: ...
    Monday: ...
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/240011

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档