简体   繁体   中英

Compute the union time ranges from two arrays of time ranges (with moment-range)

I have two arrays of moment-ranges , that represent periods when two individuals are busy.

What's the best way to compute an array which contains the union time ranges that they are busy? If two busy periods overlap for the individuals, I need the time range that represents the whole period over which they are busy.

Here's an example:

const firstBusyPeriods = [ 
  { start: "2017-04-05T10:00:00Z", end: "2017-04-05T12:00:00Z" },
  { start: "2017-04-05T14:00:00Z", end: "2017-04-05T15:00:00Z" }
]

const secondBusyPeriods = [
  { start: "2017-04-05T08:00:00Z", end: "2017-04-05T11:00:00Z" },
  { start: "2017-04-05T16:00:00Z", end: "2017-04-05T17:00:00Z" }
]

The resulting array should look like this:

const result = [
  { start: "2017-04-05T08:00:00Z", end: "2017-04-05T12:00:00Z" },
  { start: "2017-04-05T14:00:00Z", end: "2017-04-05T15:00:00Z" },
  { start: "2017-04-05T16:00:00Z", end: "2017-04-05T17:00:00Z" }
]

The result contains a union of the overlapping busy period, and then includes the two periods that don't overlap between the individuals.

Would it be best to concatenate the two arrays, sort the resulting array, and apply a reduce function?

Or would it be best to recursively iterate through one of the two arrays and produce a stack with the union and non-intersecting periods as the result?

I think I would go with concat and reduce :

 const firstBusyPeriods = [{ start: "2017-04-05T10:00:00Z", end: "2017-04-05T12:00:00Z" }, { start: "2017-04-05T14:00:00Z", end: "2017-04-05T15:00:00Z" } ]; const secondBusyPeriods = [{ start: "2017-04-05T08:00:00Z", end: "2017-04-05T11:00:00Z" }, { start: "2017-04-05T16:00:00Z", end: "2017-04-05T17:00:00Z" } ]; const isBetween = function(range, date) { return range.start < date && range.end > date; }; const rangesOverlap = function(rangeOne, rangeTwo) { return isBetween(rangeOne, rangeTwo.start) || isBetween(rangeOne, rangeTwo.end); }; const mergeRanges = function(rangeOne, rangeTwo) { let newRange = {} if (isBetween(rangeOne, rangeTwo.start)) { newRange.start = rangeOne.start; } else { newRange.start = rangeTwo.start; } if (isBetween(rangeOne, rangeTwo.end)) { newRange.end = rangeOne.end; } else { newRange.end = rangeTwo.end; } return newRange; }; const merge = function(rangeCollectionOne, rangeCollectionTwo) { let concatenatedCollections = rangeCollectionOne.concat(rangeCollectionTwo).sort((a,b) => a.start - b.start); let newCollection = concatenatedCollections.reduce((newCollection, range) => { let index = newCollection.findIndex(rangeToCheck => rangesOverlap(rangeToCheck, range)); if (index !== -1) { newCollection[index] = mergeRanges(newCollection[index], range); } else { newCollection.push(range); } return newCollection; }, []); return newCollection; } console.log(merge(firstBusyPeriods, secondBusyPeriods)); 

First I tried with a standard loop approach, I think recursion here is not even required to accomplish the task. IMHO reduce and concat way is more elegant.

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