I have an array of "shift times" and "absence times", and I'm trying to to subtract the absence time from the corresponding shift time via nested loop, to return the available shift times minus any absence time.
For example
The code subtracts the absence from the shift time and returns these two items:
I wonder if there is a simpler way to subtract the absences from the shifts times, but I've been trying to do so without success.
Also, my code does not returns the correct output. At this moment, the code retrurns:
[
{ start_time: '08:00', finish_time: '09:00' },
{ start_time: '09:30', finish_time: '10:00' },
{ start_time: '10:30', finish_time: '11:00' },
{ start_time: '12:30', finish_time: '16:00' },
{ start_time: '17:00', finish_time: '17:30' }
]
but the results should look like this:
[
{ start_time: '08:00', finish_time: '09:00' },
{ start_time: '09:30', finish_time: '10:00' },
{ start_time: '10:30', finish_time: '11:00' },
{ start_time: '12:30', finish_time: '16:00' },
{ start_time: '17:00', finish_time: '17:30' },
{ start_time: '19:00', finish_time: '20:00' }
]
const arranged_shifts = [{
'start_time' : '08:00',
'finish_time' : '10:00'
},
{
'start_time' : '10:30',
'finish_time' : '16:00'
},
{
'start_time' : '17:00',
'finish_time' : '18:00'
},
{
'start_time' : '19:00',
'finish_time' : '20:00'
}];
const absences = [{
'start_time' : '09:00',
'finish_time' : '09:30'
},
{
'start_time' : '11:00',
'finish_time' : '12:30'
},
{
'start_time' : '17:30',
'finish_time' : '18:00'
}
];
const available_times = [],
add_time = (start_time, finish_time) => {
available_times.push({
'start_time' : start_time,
'finish_time' : finish_time
});
};
const get_time_difference = (a_time, b_time) => {
return Date.parse('1970/01/01 ' + a_time) - Date.parse('1970/01/01 ' + b_time);
};
absences_loop : for (const absence of absences){
shift_loop : for (const arranged_shift of arranged_shifts){
const start_time_difference = get_time_difference(arranged_shift.start_time, absence.start_time),
finish_time_difference = get_time_difference(arranged_shift.finish_time, absence.finish_time);
if (start_time_difference === 0 && finish_time_difference > 0){
add_time(absence.finish_time, arranged_shift.finish_time);
}
else if (start_time_difference < 0 && finish_time_difference > 0){
add_time(arranged_shift.start_time, absence.start_time);
add_time(absence.finish_time, arranged_shift.finish_time)
}
else if (finish_time_difference === 0 && start_time_difference < 0){
add_time(arranged_shift.start_time, absence.start_time);
}
}
}
console.log(available_times);
Here is my take at solving this interesting problem, unfortunately ignoring your code a bit. My instinct is to convert these timestamp strings into a parsed form so that we can sort them and work with a single array of sorted and tagged timestamps. I've written some code and it seems to work great. Of course, it's not very robust, but the point is that it would be quite easy to modify/improve. The main algorithm at play here is a simple state machine, reading the "tape" of timestamps from earliest to latest.
/**
* Wraps time strings (XX:XX) into a more
* digestible format.
*/
class Timestamp {
/**
* @param {string} notation
* @param {"shiftStart" | "shiftEnd" | "absenceStart" | "absenceEnd"} type
*/
constructor(notation, type) {
if (notation.length !== 5) {
throw new Error(`Invalid length`);
}
const digits = new Uint8Array(5);
let char;
for (let i=0; i < 5; i++) {
char = notation.charCodeAt(i);
if (i === 2) {
if (char !== 0x3A) {
throw new Error(`Missing separator at index 2`);
} else {
continue;
}
}
if (char < 0x30 || char > 0x39) {
throw new Error(`Non-digit character at index ${i}`);
}
digits[i] = char - 0x30;
}
const hour = (digits[0] * 10) + digits[1];
const minute = (digits[3] * 10) + digits[4];
if (hour > 23) throw new Error(`Invalid hour ${hour}`);
if (minute > 59) throw new Error(`Invalid minute ${minute}`);
this.notation = notation;
this.hour = hour;
this.minute = minute;
this.type = type;
}
/**
* An integer which uniquely identifies this timestamp,
* used for sorting
* @returns {number}
*/
get data() {
return (this.hour * 60) + this.minute;
}
}
// Constant Data
/**
* Type definition for time records
* @typedef {{ startTime: string, endTime: string }} TimeRecord
*/
/**
* The list of shifts
* @type TimeRecord[]
*/
const SHIFTS = [
{
'startTime': '08:00',
'endTime': '10:00'
},
{
'startTime': '10:30',
'endTime': '16:00'
},
{
'startTime': '17:00',
'endTime': '18:00'
},
{
'startTime': '19:00',
'endTime': '20:00'
}
];
/**
* The list of absences
* @type TimeRecord[]
*/
const ABSENCES = [
{
'startTime': '09:00',
'endTime': '09:30'
},
{
'startTime': '11:00',
'endTime': '12:30'
},
{
'startTime': '17:30',
'endTime': '18:00'
}
];
// The Code
/**
* Returns an array holding all the
* timestamps in sorted order
* @returns {Timestamp[]}
*/
function computeTimestamps() {
const ret = [];
// Add the shifts
for (let shift of SHIFTS) {
ret.push(new Timestamp(shift.startTime, "shiftStart"));
ret.push(new Timestamp(shift.endTime, "shiftEnd"));
}
// Add the absences
for (let absence of ABSENCES) {
ret.push(new Timestamp(absence.startTime, "absenceStart"));
ret.push(new Timestamp(absence.endTime, "absenceEnd"));
}
// Sort the timestamps
ret.sort((a, b) => a.data - b.data);
return ret;
}
/**
* Converts a sorted array of timestamps to
* an array of time records, showing all
* time spans where there is a shift
* but not an absence.
* @param timestamps {Timestamp[]}
* @returns {TimeRecord[]}
*/
function convertTimestamps(timestamps) {
let shiftStart;
let inShift = false;
let inAbsence = false;
const ret = [];
function submit(time) {
ret.push({
startTime: shiftStart.notation,
endTime: time.notation,
});
}
for (let time of timestamps) {
switch (time.type) {
case "shiftStart":
shiftStart = time;
inShift = true;
break;
case "shiftEnd":
if (!inShift) throw new Error("Shift end without matching start");
if (!inAbsence) submit(time);
inShift = false;
break;
case "absenceStart":
if (inShift) submit(time);
inAbsence = true;
break;
case "absenceEnd":
if (!inAbsence) throw new Error("Absence end without matching start");
shiftStart = time;
inAbsence = false;
break;
}
}
return ret;
}
/**
* Main function
*/
function main() {
const timestamps = computeTimestamps();
const records = convertTimestamps(timestamps);
console.log(records);
}
main();
An exact match to your desired output.
[
{ startTime: '08:00', endTime: '09:00' },
{ startTime: '09:30', endTime: '10:00' },
{ startTime: '10:30', endTime: '11:00' },
{ startTime: '12:30', endTime: '16:00' },
{ startTime: '17:00', endTime: '17:30' },
{ startTime: '19:00', endTime: '20:00' }
]