如何在同一JSON对象数组中比较元素并生成无冲突课程表
课程冲突检测与无冲突日程生成方案
一、数据预处理(修复格式+转换为可计算结构)
首先修正你提供的JSON格式错误(键名需加引号,值格式规范),再将原始数据转换为便于冲突判断的结构:
// 修正后的原始课程数据 const rawCourses = [ { "course code": "act 227", "course name": "science", "section": "12345", "status": "closed", "day": "1 3 5", "time": "8 AM-9AM", "final date": "3/4/2023" }, { "course code": "mat 147", "course name": "science", "section": "12346", "status": "open", "day": "2 4", "time": "11AM-12PM", "final date": "3/6/2023" }, // 更多课程数据... ]; // 数据预处理函数 function preprocessCourses(rawData) { return rawData.map(course => { // 上课日转数字数组(1=周一,2=周二...) const days = course["day"].split(" ").map(Number); // 时间转当日分钟数(便于重叠判断) const [startStr, endStr] = course["time"].split("-"); const startMinutes = convertTimeToMinutes(startStr.trim()); const endMinutes = convertTimeToMinutes(endStr.trim()); // 期末日期转Date对象 const finalDate = new Date(course["final date"]); return { ...course, days, startMinutes, endMinutes, finalDate, isAvailable: course["status"].toLowerCase() === "open" }; }); } // 辅助函数:将12小时制时间转成当日分钟数(如"8 AM" → 480) function convertTimeToMinutes(timeStr) { const [time, period] = timeStr.split(" "); let [hours, minutes] = time.split(":").map(Number); minutes = minutes || 0; if (period === "PM" && hours !== 12) hours += 12; if (period === "AM" && hours === 12) hours = 0; return hours * 60 + minutes; } // 预处理后的数据(后续逻辑基于此) const processedCourses = preprocessCourses(rawCourses);
二、冲突判断核心逻辑
编写函数判断两个课程小节是否存在冲突,冲突包含两个维度:期末日期相同、同一上课日且时间段重叠:
// 判断两个小节是否冲突 function isConflict(sectionA, sectionB) { // 1. 期末日期相同则冲突 if (sectionA.finalDate.getTime() === sectionB.finalDate.getTime()) { return true; } // 2. 存在共同上课日且时间段重叠则冲突 const hasCommonDay = sectionA.days.some(day => sectionB.days.includes(day)); if (!hasCommonDay) return false; // 时间段重叠判断:A的开始 < B的结束 且 A的结束 > B的开始 const isTimeOverlap = sectionA.startMinutes < sectionB.endMinutes && sectionA.endMinutes > sectionB.startMinutes; return isTimeOverlap; } // 判断一个小节组合是否完全无冲突 function isCombinationValid(combination) { for (let i = 0; i < combination.length; i++) { for (let j = i + 1; j < combination.length; j++) { if (isConflict(combination[i], combination[j])) return false; } } return true; }
三、生成无冲突课程组合
根据用户输入的课程代码,获取对应开放小节,生成所有可能的组合并过滤出无冲突的:
// 获取指定课程的所有开放小节 function getCourseSections(courseCode) { return processedCourses.filter(course => course["course code"] === courseCode && course.isAvailable ); } // 生成多个数组的笛卡尔积(所有小节组合) function cartesianProduct(arrays) { return arrays.reduce((acc, curr) => { return acc.flatMap(item => curr.map(subItem => [...item, subItem])); }, [[]]); } // 根据用户输入的课程代码,生成无冲突组合 function generateValidCombinations(targetCourseCodes) { // 获取每个课程的开放小节列表 const sectionLists = targetCourseCodes.map(code => getCourseSections(code)); // 过滤无开放小节的课程 const validSectionLists = sectionLists.filter(list => list.length > 0); if (validSectionLists.length !== targetCourseCodes.length) { alert("部分课程没有开放小节"); return []; } // 生成所有可能的组合,再过滤无冲突的 const allCombinations = cartesianProduct(validSectionLists); return allCombinations.filter(combination => isCombinationValid(combination)); }
四、前端交互实现示例
假设用户输入课程代码(逗号分隔),点击按钮后动态渲染无冲突日程:
<!-- HTML结构 --> <div> <input type="text" id="courseCodesInput" placeholder="输入课程代码,逗号分隔(如act 227, mat 147)"> <button onclick="handleGenerate()">生成无冲突日程</button> <div id="scheduleResults"></div> </div>
// 前端交互函数 function handleGenerate() { const input = document.getElementById("courseCodesInput").value.trim(); if (!input) { alert("请输入课程代码"); return; } const targetCourseCodes = input.split(",").map(code => code.trim()); const validCombinations = generateValidCombinations(targetCourseCodes); const resultsContainer = document.getElementById("scheduleResults"); resultsContainer.innerHTML = ""; if (validCombinations.length === 0) { resultsContainer.innerHTML = "<p>未找到无冲突的课程组合</p>"; return; } // 渲染每个无冲突日程选项 validCombinations.forEach((combination, index) => { const scheduleDiv = document.createElement("div"); scheduleDiv.className = "schedule-option"; scheduleDiv.innerHTML = ` <h3>可选日程 ${index + 1}</h3> <table border="1" cellpadding="8"> <tr> <th>课程代码</th><th>课程名称</th><th>小节</th><th>上课日</th><th>时间</th><th>期末日期</th> </tr> ${combination.map(section => ` <tr> <td>${section["course code"]}</td> <td>${section["course name"]}</td> <td>${section["section"]}</td> <td>${section.days.join("、")}</td> <td>${section["time"]}</td> <td>${section.finalDate.toLocaleDateString()}</td> </tr> `).join("")} </table> `; resultsContainer.appendChild(scheduleDiv); }); }
关键说明
- 仅处理**开放状态(open)**的小节,自动过滤closed的;
- 若课程数量较多(如超过4门),可将笛卡尔积替换为回溯法减少计算量;
- 时间转换、冲突规则可根据实际需求调整(比如支持24小时制、忽略期末日期冲突等)。
内容的提问来源于stack exchange,提问作者mimimo the great
相关产品推荐
相关产品推荐

