Skip to content

Instantly share code, notes, and snippets.

@rolandpeelen
Last active March 18, 2019 14:54
Show Gist options
  • Select an option

  • Save rolandpeelen/6b3e286bc2e8a8fdbe8d69bccd5141d3 to your computer and use it in GitHub Desktop.

Select an option

Save rolandpeelen/6b3e286bc2e8a8fdbe8d69bccd5141d3 to your computer and use it in GitHub Desktop.
// **********************************************
// Imports
// **********************************************
const R = require('ramda')
// **********************************************
// Input data
// **********************************************
console.time('timer');
const roomOpen = [
{ startTime: new Date('2018-07-02T09:00:00Z'), endTime: new Date('2018-07-02T12:00:00Z') },
{ startTime: new Date('2018-07-02T13:00:00Z'), endTime: new Date('2018-07-02T17:00:00Z') }, // ma
{ startTime: new Date('2018-07-03T09:00:00Z'), endTime: new Date('2018-07-03T12:00:00Z') },
{ startTime: new Date('2018-07-03T13:00:00Z'), endTime: new Date('2018-07-03T17:00:00Z') }, // di
{ startTime: new Date('2018-07-04T09:00:00Z'), endTime: new Date('2018-07-04T12:00:00Z') },
{ startTime: new Date('2018-07-04T13:00:00Z'), endTime: new Date('2018-07-04T17:00:00Z') }, // wo
{ startTime: new Date('2018-07-05T09:00:00Z'), endTime: new Date('2018-07-05T12:00:00Z') },
{ startTime: new Date('2018-07-05T13:00:00Z'), endTime: new Date('2018-07-05T17:00:00Z') }, // do
{ startTime: new Date('2018-07-06T09:00:00Z'), endTime: new Date('2018-07-06T12:00:00Z') } // vrij
];
const roomBlocked = [
// ma
{ startTime: new Date('2018-07-03T09:00:00Z'), endTime: new Date('2018-07-03T11:00:00Z') }, // di
// wo
{ startTime: new Date('2018-07-05T09:00:00Z'), endTime: new Date('2018-07-05T10:00:00Z') },
{ startTime: new Date('2018-07-05T14:00:00Z'), endTime: new Date('2018-07-05T16:30:00Z') }, // do
{ startTime: new Date('2018-07-06T09:00:00Z'), endTime: new Date('2018-07-06T11:15:00Z') } // vrij
];
// **********************************************
// Root Functions
// t for time, a for array, i for item, a,b for comparison
// **********************************************
// Takes epoch milliseconds and converts it to minutes
const msToMinutes = t => t / 1000 / 60;
// Takes minutes and converts them to epoch milliseconds
const minutesToMs = t => t * 1000 * 60;
// Takes a date and converts it to epoch
const dateToEpoch = i => i.getTime();
// Takes an epoch and converts it to date
const epochTodate = i => new Date(i);
// Takes two arguments, adds one to the first and checks if that's equal to the second
const isEqualToNextPlusOne = (a, b) => a + 1 === b;
// Check if an array does not include an item
const doesNotInclude = R.curry((a, i) => !a.includes(i));
// Flattens recursively
const deepFlatten = a => [].concat(...a.map(i => (Array.isArray(i) ? deepFlatten(i) : i)));
// **********************************************
// Composed Functions
// **********************************************
// Takes amount of minutes since 1970, converts it to a date object
const toMsAndDate = R.compose(epochTodate, minutesToMs);
// Takes a date object, converts it to minutes since 1970
const epochAndToMinutes = R.compose(msToMinutes, dateToEpoch);
// Takes an item and maps it to a start / end date object {startTime: epochMinutes } --> {startTime: Date }
const toDates = i => ({ startTime: toMsAndDate(i[0]), endTime: toMsAndDate(i[i.length - 1]) });
// Takes one start / endTime tuple and converts it to a range (including the last time -- thats the + 1)
const getMinuteRange = ({ startTime, endTime }) => R.range(epochAndToMinutes(startTime), epochAndToMinutes(endTime) + 1);
// Takes an array, passes each item through the getMinuteRange, flattens that and outputs it (creating one array of values)
const getMinuteRangeForArray = a => deepFlatten(a.map(getMinuteRange));
const getActualRange = (openTimes, closedTimes) => {
// Convert the rangeOpen and rangeBlocked to arrays of numbers
const rangeOpen = getMinuteRangeForArray(openTimes);
const rangeBlocked = getMinuteRangeForArray(closedTimes);
// Filter the ones that don't match
const intersection = rangeOpen.filter(doesNotInclude(rangeBlocked));
// from those ranges group them to have blocks of ranges. IE;
// [1,2,3,4,5,6,7,12,13,14,15,16] to:
// [[1,2,3,4,5,6,7],[12,13,14,15,16]]
// The toDates function takes the first of those groups and the last of those groups and maps them
// to a startdate and endDate
return R.groupWith(isEqualToNextPlusOne, intersection).map(toDates)
};
// Actual use
const roomActual = getActualRange(roomOpen, roomBlocked);
console.timeEnd('timer');
// **********************************************
// Output data (~19ms)
// **********************************************
// [ { startTime: 2018-07-02T09:00:00.000Z, endTime: 2018-07-02T12:00:00.000Z },
// { startTime: 2018-07-02T13:00:00.000Z, endTime: 2018-07-02T17:00:00.000Z },
// { startTime: 2018-07-03T11:01:00.000Z, endTime: 2018-07-03T12:00:00.000Z },
// { startTime: 2018-07-03T13:00:00.000Z, endTime: 2018-07-03T17:00:00.000Z },
// { startTime: 2018-07-04T09:00:00.000Z, endTime: 2018-07-04T12:00:00.000Z },
// { startTime: 2018-07-04T13:00:00.000Z, endTime: 2018-07-04T17:00:00.000Z },
// { startTime: 2018-07-05T10:01:00.000Z, endTime: 2018-07-05T12:00:00.000Z },
// { startTime: 2018-07-05T13:00:00.000Z, endTime: 2018-07-05T13:59:00.000Z },
// { startTime: 2018-07-05T16:31:00.000Z, endTime: 2018-07-05T17:00:00.000Z },
// { startTime: 2018-07-06T11:16:00.000Z, endTime: 2018-07-06T12:00:00.000Z } ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment