Javascript Array sort and make same schedule as range of days

Viewed 285

I'm provided with CSV of office schedules with dash range for the days with the same schedule.

For example, today is Thursday:

Mon 3:00am - 9:00pm, Tue - Thu 5:30am - 9:30pm, Fri 7:15am - 10:00pm, Sat 8:00am - 3:00pm, Sun Closed

I need to remove Sunday closed schedule and show first Thursday first for example if today is Thursday

My expected output is to something like this:

Tue - Thu 5:30am - 9:30pm, Fri 7:15am - 10:00pm, Sat 8:00am - 3:00pm, Mon 3:00am - 9:00pm

Some examples and expected results if today is Thursday (current day):

'Mon 4:00am - 9:00pm, Tue 6:00am - 9:30pm, Wed - Fri 5:30am - 9:30pm, Sat 8:00am - 3:00pm, Sun Closed' Expected result: 'Tue 6:00am - 9:30pm, Wed - Fri 5:30am - 9:30pm, Sat 8:00am - 3:00pm, Mon 4:00am - 9:00pm'

'Wed 5:30am - 9:30pm, Thu 6:30am - 4:00pm, Fri - Tue 8:00am - 3:00pm, Sun Closed' Expected result: 'Thu 6:30am - 4:00pm, Fri - Tue 8:00am - 3:00pm, 'Wed 5:30am - 9:30pm'

'Mon - Tue 5:30am - 9:30pm, Wed 6:30am - 4:00pm, Thu - Sat 8:00am - 3:00pm, Sun Closed' Expected result: 'Thu - Sat 8:00am - 3:00pm, Mon - Tue 5:30am - 9:30pm, Wed 6:30am - 4:00pm'

My current code partially working:

function sortDays(days) {
    var dayOfWeek = new Date().getDay();
    var list = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    var sortedList = list.slice(dayOfWeek).concat(list.slice(0, dayOfWeek));
    return days.sort(function(a,b) { return sortedList.indexOf(a) > sortedList.indexOf(b); });
}

function removeA(arr) {
    var what, a = arguments, L = a.length, ax;
    while (L > 1 && arr.length) {
        what = a[--L];
        while ((ax= arr.indexOf(what)) !== -1) {
            arr.splice(ax, 1);
        }
    }
    return arr;
}

function getDaysRange(first, last) {
    // var week = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
    var week = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

    // Find first day
    var firstIndex = week.indexOf(first);
    // Shift array so that first day is index 0
    week = week.concat(week.splice(0, firstIndex));
    // Find last day
    var lastIndex = week.indexOf(last);

    // Cut from first day to last day and convert to CSV string
    // return week.slice(0, lastIndex + 1).toString();
    return week.slice(0, lastIndex + 1);
}



function reorderSchedule(schedule) {

    var detailedSchedule = [];
    var daysInWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    var scheduleExploded = schedule.split(',');
    var schedulehtms = '';

    console.log('===============================>>>>>>>>>>>>>>>>>>>');
    for (var i = 0, len = scheduleExploded.length; i < len; i++) {
        schedulehItem = scheduleExploded[i].trim();
        console.log(schedulehItem + ' = LOOP NUMBER ' + i + ' >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');

        var count = (schedulehItem.match(/-/g) || []).length;
        console.log(count + " piece(s) of - found inside " + schedulehItem);

        if (count >= 2) {
            console.log(schedulehItem + ' is a range! ===============');

            // break down the days range
            var search = schedulehItem.search(/\d/);

            var scheduleOfRange = schedulehItem.substring(search, schedulehItem.length);
            console.log(scheduleOfRange);
            var daysRange = schedulehItem.substring(0, search - 1);
            console.log(daysRange);

            var arr = daysRange.split('-');
            var first = arr[0].trim();
            var second = arr[1].trim();

            var daysRange = getDaysRange(first, second);
            daysRange = removeA(daysRange, 'Sun');
            console.log(daysRange);
            console.log('>>>>>>>');

            for (x = 0; x < daysRange.length; x++) { 
                detailedSchedule.push(daysRange[x] + ' ' + scheduleOfRange);
            }

        } else {
            // insert the schedule in the final array
            detailedSchedule.push(schedulehItem);
        }

    }
    console.log('===============================>>>>>>>>>>>>>>>>>>>');
    console.log('=============');
    console.log(detailedSchedule);
    console.log('=============');

    var origDaysOnlyOrder = [];
    var daysWithScheduleOrder = [];
    var sortedDays = sortDays(daysInWeek);

    for (i = 0; i < detailedSchedule.length; i++) {        

        var myNewStr = detailedSchedule[i].substr(0, 3);
        // origDaysOnlyOrder.push(myNewStr);
        daysWithScheduleOrder[myNewStr] = detailedSchedule[i];
    }

    console.log('vvvvvvvvvvvvvvvvvvvvvvvvvvvv');
    console.log(daysWithScheduleOrder);
    console.log(sortedDays);

    var finalArray = [];
    for (i = 0; i < sortedDays.length; i++) {
        // don't insert undefined values
        if (typeof(daysWithScheduleOrder[sortedDays[i]]) != 'undefined') {
            // finalArray.push(daysWithScheduleOrder[sortedDays[i]]);
            finalArray[sortedDays[i]] = daysWithScheduleOrder[sortedDays[i]].substr(3).trim();
        }
    }

    // remove
    console.log(finalArray);
    console.log('######################>>>');

    // now in correct order then bring back the dash in the original range, check for same schedules then create the range
    // find first

    var scheduleObj = {};
    for (var key in finalArray) {
        var value = finalArray[key];
        // console.log(key, value);
        scheduleObj[key] = value;
    }
    console.log(scheduleObj);


    var hash = Object.create(null);
    var result = Object.create(null);

    Object.keys(scheduleObj).forEach(k => {
        var grp = scheduleObj[k];

        (grp in hash) ? hash[grp].push(k) : (hash[grp] = [k]);
    });

    for (key in hash) {
        if (hash[key].length > 1) {
            result[key] = hash[key].toString();
        }
    }

    console.log(hash);
    console.log('hash sa taas');


    var finalScheduleArray = [];
    // loop hash
    Object.keys(hash).forEach(function(key) {
        // console.log(key, result[key]);
        console.log(key, hash[key]);
        var my_key = key;
        var my_value = hash[key];


        if (my_value.length == 1) {
            finalScheduleArray[my_value] = my_key;
        } else {

            var rangeArray = [];
            for (i = 0; i < my_value.length; i++) {
                console.log(my_value[i]);
                rangeArray.push(my_value[i]);

            }
            console.log(rangeArray);

            var firstItem = rangeArray[0];
            var lastItem = rangeArray[rangeArray.length-1];
            finalScheduleArray[firstItem + ' - ' + lastItem] = my_key;
        }

    });

    console.log(finalScheduleArray);
    console.log('finalScheduleArray ----------');
    return 'zzz';
}

$(function() {
    // var schedule = 'Mon 4:00am - 9:00pm, Tue 6:00am - 9:30pm, Wed - Fri 5:30am - 9:30pm, Sat 8:00am - 3:00pm, Sun Closed'; // MALI Wed - Fri
    // var schedule = 'Mon 4:30am - 9:30pm, Tue 5:30am - 9:30pm, Wed 5:00am - 9:30pm, Thu - Sat 8:00am - 3:00pm, Sun Closed'; // tama Thu - Sat
    var schedule = 'Mon 3:00am - 9:00pm, Tue - Thu 5:30am - 9:30pm, Fri 7:15am - 10:00pm, Sat 8:00am - 3:00pm, Sun Closed'; // MALI Tue - Thu
    console.log(reorderSchedule(schedule));
});
2 Answers

The following will probably work, it doesn't remove 'Sun' not sure if you just want that day removed or because it happens to be closed.

const week = [
  'Sun',
  'Mon',
  'Tue',
  'Wed',
  'Thu',
  'Fri',
  'Sat',
];
const withDayPair = (item) => {
  //add [dayOneIndex, dayTwoIndex] to value
  const parts = item
    .split('-')
    .map((s) => s.trim())
    .map((s) => s.substr(0, 3));
  if (parts.length > 2) {
    //this value has day range (for example Mon - Wed)
    return [
      parts.slice(0, 2).map((p) => week.indexOf(p)),
      item,
    ];
  }
  return [[week.indexOf(parts[0]), -1], item]; //only one day, set second day to -1
};
const assignValue = (current) => ([[a, b], item]) => {
  //assign a value for sorting. If both values are smaller than current (earlier in the week)
  //  then add 10 to the index of the value in the array
  const val = a < current && b < current ? a + 10 : a;
  return [val, item];
};
const sortWith = (str, day) =>
  str
    .split(',')
    .map((s) => s.trim())
    .filter((s) => !s.startsWith('Sun'))//remove sunday
    //having a string representing days of week is not the best way to represent
    //  the data types used in your application
    .map(withDayPair)
    //now the values will be [[1,3],'Mon - Wed ....']
    .map(assignValue(week.indexOf(day))) //the [1,3] part is replaced with one number
    .sort(([a], [b]) => a - b) //sorting on that number
    .map(([_, item]) => item); //remove the number
const testVal =
  'Mon - Tue 5:30am - 9:30pm, Wed 6:30am - 4:00pm, Thu - Sat 8:00am - 3:00pm, Sun Closed';
console.log(
  //test for every day in the week
  ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
    .map(
      (day) =>
        `Day:${day} == ${sortWith(testVal, day).join(',')}`,
    )
    .join('\n'),
);

This is a fresh approach, thinking about how I'd solve this problem:

const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']

const parseDay = (daySched) => {
  const parts = daySched.match(/^(\w{3})( - (\w{3}))? (.+)$/)
  return {
    start: parts[1],
    end: parts[3] || parts[1],
    times: parts[4]
  }
}
const rotate = (idx) => (arr) => arr.slice(idx).concat(arr.slice(0, idx))

const makeSched = (schedStr) => {
  const schedule = schedStr.split(/\,\s+/).map(parseDay)
  return rotate(schedule.length - 1)(schedule) // put Sun first
}

const rotateSched = (schedule, day) => {
  const start = schedule.findIndex(d => days.indexOf(d.end) >= day)
  return rotate(start)(schedule) // perhaps not strictly necessary
}

const formatOpenHours = (schedule) => schedule
  .map(day => `${day.start}${day.end === day.start ? '' : ' - ' + day.end} ${day.times}`)
  .join(', ')

// This testable function is what I'd export
const reorder = (schedStr, day) => formatOpenHours(
  rotateSched(
    makeSched(schedStr).filter(day => day.times !== 'Closed'), 
    day
  )
)

// I wouldn't include this function.
// The dependence on current date makes it hard to test
const reorderSchedule = (schedStr) => reorder(schedStr, new Date().getDay())

// Demos

console.log(reorderSchedule(
  'Mon 3:00am - 9:00pm, Tue - Thu 5:30am - 9:30pm, Fri 7:15am - 10:00pm, Sat 8:00am - 3:00pm, Sun Closed'
))

console.log(reorder(
  'Mon 4:00am - 9:00pm, Tue 6:00am - 9:30pm, Wed - Fri 5:30am - 9:30pm, Sat 8:00am - 3:00pm, Sun Closed', 
  2
))

console.log(reorder(
  'Wed 5:30am - 9:30pm, Thu 6:30am - 4:00pm, Fri - Tue 8:00am - 3:00pm, Sun Closed',
  4
))

console.log(reorder(
  'Mon - Tue 5:30am - 9:30pm, Wed 6:30am - 4:00pm, Thu - Sat 8:00am - 3:00pm, Sun Closed',
  4
))

This works by creating an intermediate structure, an array with entries like {start: 'Tue', end: Thu, times: '5:30am - 9:30pm'} or {start: 'Sun', end: 'Sun', times: 'Closed'}, and then rotating this depending on the selected day.

One point I'd like to make is that using the current date in the main function makes it hard to test. I would write something which accepts the day (reorder here) and use that as the main function, possibly wrapping it with something like reorderSchedule if necessary. Notice how little is done in reorderSchedule; if that little bit of date fiddling can be pushed up to the calling function, then you don't need this wrapper, and the code will be more testable.

Related