Digamos que tengo una matriz como la siguiente, ¿cuál sería el mejor método para separarlos en matrices individuales si los tiempos se superponen? Estoy usando el momento, pero no estoy seguro de cómo abordar esto.
Sé que tengo que ordenar la matriz inicialmente.
datos:
const timetable = [ { class: 'one', start: '2021-11-16T09:00:00', end: '2021-11-16T10:00:00' }, { class: 'two', start: '2021-11-16T010:00:00', end: '2021-11-16T11:00:00' }, { class: 'three', start: '2021-11-16T09:00:00', end: '2021-11-16T10:00:00' }, ];esperado:
const timetable = [ [ { class: 'one', start: '2021-11-16T09:00:00', end: '2021-11-16T10:00:00' }, { class: 'two', start: '2021-11-16T010:00:00', end: '2021-11-16T11:00:00' }, ], [ { class: 'three', start: '2021-11-16T09:00:00', end: '2021-11-16T10:00:00', }, ], ];Un enfoque genérico tiene que funcionar recursivamente a través de cualquier timetable original dado (matriz de origen) para detectar/generar la mayor cantidad de tablas de tiempo, cada una de las cuales presenta solo elementos de rango de tiempo que no se superponen.
Una implementación recursiva se llamaría a sí misma repetidamente siempre que, dentro de una matriz procesada, aún se encuentren elementos de rango de tiempo superpuestos.
Parte del enfoque es que tal función autorrecursiva crea una copia superficial de la tabla de tiempo pasada y también la ordena exactamente una vez, en el momento de ser llamada inicialmente.
function parseTime(value) { return new Date(value).getTime(); } function getParsedTimeRangeFromItem({ start, end }) { return { start: parseTime(start), end: parseTime(end), } } function orderByTimeRangeAscending(a, b) { const { start: aStart, end: aEnd } = getParsedTimeRangeFromItem(a); const { start: bStart, end: bEnd } = getParsedTimeRangeFromItem(b); return (aStart - bStart) || (aEnd - bEnd); } function createTimetablesOfNonOverlappingTimeRanges(timetable, result) { // at initial call time only ... if (!Array.isArray(result)) { // ... create the result array ... result = []; // ... and also a shallow and sorted copy // of the initially passed `timetable`. timetable = [...timetable].sort(orderByTimeRangeAscending); } const rejected = []; let idx = -1; let item, nextItem; while ( (item = timetable[++idx]) && (nextItem = timetable[idx + 1]) ) { // detect `nextItem` as overlapping time range item ... if (parseTime(item.end) > parseTime(nextItem.start)) { // ... and reject it from the `timetable` reference. rejected.push(timetable.splice((idx + 1), 1)[0]) --idx; } } // add the sanitized but mutated `timetable` to `result`. result.push(timetable); // in case of any rejected time range item trigger self recursion. if (rejected.length >= 1) { result = createTimetablesOfNonOverlappingTimeRanges(rejected, result); } return result; } const timetable = [ { class: 'one', start: '2021-11-16T09:00:00', end: '2021-11-16T10:00:00' }, { class: 'two', start: '2021-11-16T10:00:00', end: '2021-11-16T11:00:00' }, { class: 'three', start: '2021-11-16T09:00:00', end: '2021-11-16T10:00:00' }, { class: 'four', start: '2021-11-16T09:00:00', end: '2021-11-16T10:00:00' }, { class: 'five', start: '2021-11-16T10:00:00', end: '2021-11-16T11:00:00' }, { class: 'six', start: '2021-11-16T09:00:00', end: '2021-11-16T10:00:00' }, ]; console.log( '[...timetable].sort(orderByTimeRangeAscending) ...', [...timetable] .sort(orderByTimeRangeAscending) ); console.log( 'createTimetablesOfNonOverlappingTimeRanges(timetable) ...', createTimetablesOfNonOverlappingTimeRanges(timetable) ); console.log('un-mutated original source array ...', { timetable }); .as-console-wrapper { min-height: 100%!important; top: 0; }Divídalo en partes más pequeñas y aborde cada problema. No estoy seguro de cuál es tu plan si terminas con varias clases superpuestas. Este código solo devuelve una matriz con dos elementos: clases únicas y clases superpuestas, no sigue agregando nuevos elementos para cada clase infractora.
const timetable = [ { class: "one", start: "2021-11-16T09:00:00", end: "2021-11-16T10:00:00" }, { class: "two", start: "2021-11-16T10:00:00", end: "2021-11-16T11:00:00" }, { class: "three", start: "2021-11-16T09:00:00", end: "2021-11-16T10:00:00" }, ]; // get an object, return a new object with the times as // timestamps, rather than time strings // (you don't need Moment.JS, JavaScript has .getTime()) const parseTimes = el => { const hour = {...el}; hour.start = new Date(el.start).getTime(); hour.end = new Date(el.end).getTime(); return hour; }; // given two objects, see if one is fully before or after // another. If not, they overlap. const overlaps = (a, b) => { if (a.start <= b.start && a.end <= b.start) return false; if (a.end >= b.end && a.start >= b.end) return false; return true; }; // given an array and a starting place, figure out if any // of the objects overlap any of the previous objects const overlapsPrevious = (arr, idx) => { const a = parseTimes(arr[idx]); for (let ix = 0; ix <= idx; ix++) { const b = parseTimes(arr[ix]); if (a.class !== b.class && overlaps(a, b)) return true; } return false; }; // given an array, return a new array with non-overlaping // objects and objects that overlap const split = (arr) => { const unique = []; const overlap = []; for(let ix = 0; ix < arr.length; ix++) { if(overlapsPrevious(arr, ix)) overlap.push(arr[ix]); else unique.push(arr[ix]); } return [unique, overlap]; } // display the result console.log(split(timetable));