-2

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

  • there's an absence that starts at 09:00 and finishes at 09:30 and
  • the first shift time starts at 08:00 and finishes at 10:00.

The code subtracts the absence from the shift time and returns these two items:

  1. starts at 08:00 and finishes at 09:00
  2. starts at 09:30 and finishes at 10:00

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

My code is here:

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

2
  • How to debug small programs: StackOverflow is a question-and-answer site for specific questions about actual code; “I wrote some buggy code that I can’t fix” is not a question, it’s a story, and not even an interesting story. Commented Jul 25 at 17:50
  • A nested loops shouldn't be necessary. You could iterate both arrays at once and accumulate the time. Commented Jul 25 at 17:53

1 Answer 1

2

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

Output

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' }
]
Sign up to request clarification or add additional context in comments.

1 Comment

That's absolutely fantastic and genius, the concept make complete sense, I wouldn't of come up with the solution So thank you very much for all your hard work and efferts I'm looking forward to playing with this tomorrow

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.