javascriptnode.jsmomentjstimeslots

Finding the nearest free time slot from now if free slots in some working days are given


I have a function that takes an array of objects, where each object contains a 'day' key (representing the day of the week) and an 'freeSlots' key (representing an array of available time slots as strings in 'h:mm a' format). The function aims to find and return the nearest valid free time slot from the current date and time. If no nearest slot is available for the current week, it should return "wait for next week." However, when searching for the nearest slot on Tuesday, the function is returning an unexpected result.

function findNearestFreeSlot(slotsArray) {
    const currentDate = moment();
    let nearestDayDiff = Infinity;
    let nearestSlot = null;

    for (const daySlots of slotsArray) {
        const { day, freeSlots } = daySlots;

        for (const slot of freeSlots) {
            const slotDateTime = moment(slot, 'h:mm a').day(day);

            // Check if the slot is on or after the current date
            if (slotDateTime.isSameOrAfter(currentDate)) {
                const diffDays = Math.abs(currentDate.diff(slotDateTime, 'days'));

                if (diffDays < nearestDayDiff || (diffDays === nearestDayDiff && slotDateTime.isBefore(nearestSlot))) {
                    nearestDayDiff = diffDays;
                    nearestSlot = slotDateTime;
                }
            }
        }
    }

    return nearestSlot ? nearestSlot.format('ddd, h:mm a') : "wait for next week";
}

I have used this array for testing the above function

const freeSlotsArray = [
    {
        day: 'wed',
        freeSlots: ['12:00 pm', '1:00 pm', '1:30 pm', '2:30 pm', '3:00 pm', '3:30 pm', '4:30 pm', '5:00 pm', '6:30 pm', '7:00 pm']
    },
    {
        day: 'sat',
        freeSlots: ['7:00 am', '7:30 am', '8:00 am', '9:00 am', '10:00 am', '10:30 am', '11:30 am', '12:00 pm', '12:30 pm']
    },
    {
        day: 'thu',
        freeSlots: ['12:00 pm', '1:00 pm', '1:30 pm', '2:30 pm', '4:00 pm', '5:30 pm', '6:30 pm', '7:00 pm']
    },
    {
        day: 'mon',
        freeSlots: ['4:00 pm', '4:30 pm', '6:00 pm', '6:30 pm', '7:30 pm', '8:00 pm', '8:30 pm', '9:30 pm']
    },
];


const nearestFreeSlot = findNearestFreeSlot(freeSlotsArray);
console.log(nearestFreeSlot);

When searching for the nearest slot on Tuesday, the expected output should be "Wed, 12:00 pm" instead of "Wed, 4:30 pm".


Solution

  • The problem is that you pass a wrong parameter to .day(), pass it a number ranging from 0 to 6 (or the full name of the day, but I'm not sure how well this alternative works).

    Below I put a more compact function and your correct one.

    const fakeDay = 2; // 2 is Tuesday, change it for simulate a day sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6
    
    function findNearestFreeSlot(slotsArray) {
      const currentDate = moment().day(fakeDay);
      let nearestDayDiff = Infinity;
      let nearestSlot = null;
    
      const indexDays = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 }; // Add this index number: day
    
      for (const daySlots of slotsArray) {
        const { day, freeSlots } = daySlots;
    
        for (const slot of freeSlots) {
          const slotDateTime = moment(slot, 'h:mm a').day(indexDays[day]); // <---- use the index
    
          // Check if the slot is on or after the current date
          if (slotDateTime.isSameOrAfter(currentDate)) {
            const diffDays = Math.abs(currentDate.diff(slotDateTime, 'days'));
    
            if (diffDays < nearestDayDiff || (diffDays === nearestDayDiff && slotDateTime.isBefore(nearestSlot))) {
              nearestDayDiff = diffDays;
              nearestSlot = slotDateTime;
            }
          }
        }
      }
    
      return nearestSlot ? nearestSlot.format('ddd, h:mm a') : "wait for next week";
    }
    
    
    function findNearestFreeSlotMyTry(slotsArray) {
      const currentDate = moment().day(fakeDay);
    
      const indexDays = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
    
      const listSlots = slotsArray
        .reduce((prev, { day, freeSlots }) => { // Converto the object of free slots in to array with the difference
          for (const slot of freeSlots) {
            const dateSlot = moment(slot, 'h:mm a').day(indexDays[day]);
            const diff = dateSlot.diff(currentDate);
            if (diff >= 0) prev.push({ diff, date: dateSlot.format('ddd, h:mm a') }); // Get elements only if is in the fucture
          }
          return prev;
        }, [])
        .sort((a, b) => a.diff < b.diff ? -1 : 1); //  sort the array by diff, from the nearlest to further
    
      return listSlots?.[0]?.date || 'wait for next week'; // Return the date or the string
    }
    
    const freeSlotsArray = [
      {
        day: 'wed',
        freeSlots: [/* '12:00 am', */ '12:00 pm', '1:00 pm', '1:30 pm', '2:30 pm', '3:00 pm', '3:30 pm', '4:30 pm', '5:00 pm', '6:30 pm', '7:00 pm']
      },
      {
        day: 'sat',
        freeSlots: ['7:00 am', '7:30 am', '8:00 am', '9:00 am', '10:00 am', '10:30 am', '11:30 am', '12:00 pm', '12:30 pm']
      },
      {
        day: 'thu',
        freeSlots: ['12:00 pm', '1:00 pm', '1:30 pm', '2:30 pm', '4:00 pm', '5:30 pm', '6:30 pm', '7:00 pm']
      },
      {
        day: 'mon',
        freeSlots: ['4:00 pm', '4:30 pm', '6:00 pm', '6:30 pm', '7:30 pm', '8:00 pm', '8:30 pm', '9:30 pm']
      }
    ];
    
    const nearestFreeSlot = findNearestFreeSlot(freeSlotsArray);
    console.log('-----> ', nearestFreeSlot);
    
    const nearestFreeSlotMyTry = findNearestFreeSlotMyTry(freeSlotsArray);
    console.log('-----> ', nearestFreeSlotMyTry);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>