拆分一组日期范围项目,每个项目都有一个开始和结束时间戳,这些时间戳可以相互重叠成单独的非重叠范围数组?

Split an array of date range items each with a start and an end timestamp which can overlap each other into separate arrays of non overlapping ranges?

假设我有一个如下所示的数组,如果时间重叠,最好的方法是将它们分成单独的数组?我正在使用 moment 但有点不确定如何解决这个问题。

我知道我必须先对数组进行排序。

数据:

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' },
];

预计:

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',
    },
  ],
];

将其分解成更小的块并解决每个问题。如果您最终遇到多个重叠 class,我不确定您的计划是什么。这段代码只是 returns 一个包含两个元素的数组:独特的 classes 和重叠的 classes,它不会为每个违规的 class.[= 添加新元素。 11=]

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));

通用方法必须通过任何给定的原始 timetable(源数组)递归地工作,以便 detect/generate 尽可能多的时间 tables 每个特征只是不重叠时间范围项目。

只要在已处理的数组中仍然找到重叠的时间范围项,递归实现就会重复调用自身。

部分方法是,这样的自递归函数确实创建了经过时间的浅表副本table,并且在最初被调用时确实对其进行了一次精确排序。

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; }