Search code examples
javascriptarraysmultidimensional-arraymomentjsunix-timestamp

How do I assign an array time ranges to each day of the week?


What I want to create using the my code is an array of 7 objects as illustrated below:

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]

These objects are 7 because they represent each day of the week, as seen below, when the array of 7 object is expanded:

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
  0: {Sundays: Array(6)}
  1: {Mondays: Array(6)}
  2: {Tuesdays: Array(6)}
  3: {Wednesdays: Array(6)}
  4: {Thursdays: Array(6)}
  5: {Fridays: Array(6)}
  6: {Saturdays: Array(6)}

I have divided each day into 6 time ranges, namely:

00:00 - 04:00,
04:00 - 08:00,
08:00 - 12:00,
12:00 - 16:00,
16:00 - 20:00 and finally
20:00 - 00:00.

...and assigned the time ranges to each of the days. The times are saved in timestamp format.

For clarity purposes, I have randomly chosen to expand the Mondays objects, to illustrate what I mean:

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
  0: {Sundays: Array(6)}
  1: Mondays: Array(6)
       0: {T_00:00_Start: 1702242000, T_04:00_End: 1702256400}
       1: {T_04:00_Start: 1702256400, T_08:00_End: 1702270800}
       2: {T_08:00_Start: 1702270800, T_12:00_End: 1702285200}
       3: {T_12:00_Start: 1702285200, T_16:00_End: 1702299600}
       4: {T_16:00_Start: 1702299600, T_20:00_End: 1702314000}
       5: {T_20:00_Start: 1702314000, T_00:00_End: 1702328400}
  .
  .
  .  

The following architecture suggests that a duration of 7 days (one week) has been fed into the array of days:

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
  0: {Sundays: Array(6)}
  1: {Mondays: Array(6)}
  2: {Tuesdays: Array(6)}
  3: {Wednesdays: Array(6)}
  4: {Thursdays: Array(6)}
  5: {Fridays: Array(6)}
  6: {Saturdays: Array(6)}

However, if today was a Monday, and the duration was 8 days instead, the array of objects would look like the following (Note that Monday now holds an array of 12 and not 6):

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
  0: {Sundays: Array(6)}
  1: {Mondays: Array(12)}
  2: {Tuesdays: Array(6)}
  3: {Wednesdays: Array(6)}
  4: {Thursdays: Array(6)}
  5: {Fridays: Array(6)}
  6: {Saturdays: Array(6)}

If I was to expand the Mondays objects, it would reveal 12 objects. The first 6 would represent today (this Monday) while the next 6 would represent the next weeks Monday, as seen below:

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
  0: {Sundays: Array(6)}
  1: Mondays: Array(12)

       // Today (Current Monday)
       0: {T_00:00_Start: 1702242000, T_04:00_End: 1702256400}
       1: {T_04:00_Start: 1702256400, T_08:00_End: 1702270800}
       2: {T_08:00_Start: 1702270800, T_12:00_End: 1702285200}
       3: {T_12:00_Start: 1702285200, T_16:00_End: 1702299600}
       4: {T_16:00_Start: 1702299600, T_20:00_End: 1702314000}
       5: {T_20:00_Start: 1702314000, T_00:00_End: 1702328400}

       // Monday the following week
       6: {T_00:00_Start: 1702846800, T_04:00_End: 1702861200}
       7: {T_04:00_Start: 1702861200, T_08:00_End: 1702875600}
       8: {T_08:00_Start: 1702875600, T_12:00_End: 1702890000}
       9: {T_12:00_Start: 1702890000, T_16:00_End: 1702904400}
       10: {T_16:00_Start: 1702904400, T_20:00_End: 1702918800}
       11: {T_20:00_Start: 1702918800, T_00:00_End: 1702933200}
  .
  .
  .  

If again today was a Monday and the duration was 14 meaning 2 weeks, the structure is expected to look like this:

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
  0: {Sundays: Array(12)}
  1: {Mondays: Array(12)}
  2: {Tuesdays: Array(12)}
  3: {Wednesdays: Array(12)}
  4: {Thursdays: Array(12)}
  5: {Fridays: Array(12)}
  6: {Saturdays: Array(12)}

Why am I failing to get my code below to achieve this?

My code is failing because, if you use a duration of "8 days", the array of time ranges that are stored under each day will show an over "42 arrays".

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
   0: {Sundays: Array(42)}
   1: {Mondays: Array(84)}
   2: {Tuesdays: Array(42)}
   3: {Wednesdays: Array(42)}
   4: {Thursdays: Array(42)}
   5: {Fridays: Array(42)}
   6: {Saturdays: Array(42)}

The desired output for a duration of "8 days" should be:

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
   0: {Sundays: Array(6)}
   1: {Mondays: Array(12)}
   2: {Tuesdays: Array(6)}
   3: {Wednesdays: Array(6)}
   4: {Thursdays: Array(6)}
   5: {Fridays: Array(6)}
   6: {Saturdays: Array(6)}

another example is, for "14 days or one week" as a duration, the desired output should be:

(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
   0: {Sundays: Array(12)}
   1: {Mondays: Array(12)}
   2: {Tuesdays: Array(12)}
   3: {Wednesdays: Array(12)}
   4: {Thursdays: Array(12)}
   5: {Fridays: Array(12)}
   6: {Saturdays: Array(12)}

Furthermore, the time ranges, in timestamps that are stored under each of these days should be relative that the parent day, my code fails to achieve this.

For instance, note how none of the timestamps stored under Thursday, actually indicate Thursdays.

Thursdays: Array(42)
    0: {T_00:00_Start: 1702242000, T_04:00_End: 1702256400}
    1: {T_04:00_Start: 1702256400, T_08:00_End: 1702270800}
    2: {T_08:00_Start: 1702270800, T_12:00_End: 1702285200}
    3: {T_12:00_Start: 1702285200, T_16:00_End: 1702299600}
    4: {T_16:00_Start: 1702299600, T_20:00_End: 1702314000}
    5: {T_20:00_Start: 1702314000, T_00:00_End: 1702328400}

Following is my code that you can copy & paste & run in your browser console.

The duration variable in my code is referred to as durationOfCampaignInDays:

const daysArrayDummy = ['Sundays', 'Mondays', 'Tuesdays', 'Wednesdays', 'Thursdays', 

    'Fridays', 'Saturdays'];
    
    const startOfTheDay = moment().startOf("day");
    
    const durationOfCampaignInDays = 8; // Replace with your actual variable
    const updatedDaysArray = [];
    
    for (let i = 0; i < daysArrayDummy.length; i++) {
      const day = daysArrayDummy[i];
      console.log(`Processing ${day}`);
      const dailyTimeRanges = [];
    
      const isSpecialDay = i === 1; // Adjust the condition based on the special days
      const numberOfTimeRanges = isSpecialDay ? 12 : 6; // Adjust the number of time ranges
    
      for (let j = 0; j < durationOfCampaignInDays; j++) {
        const dayStart = moment(startOfTheDay).add(j, 'days').startOf("day");
    
        for (let k = 0; k < numberOfTimeRanges; k++) {
          const rangeStart = moment(dayStart).add(k * 4, 'hours');
          const rangeEnd = moment(dayStart).add((k + 1) * 4, 'hours');
    
          const startTimestamp = Math.floor(rangeStart.valueOf() / 1000);
          const endTimestamp = Math.floor(rangeEnd.valueOf() / 1000);
    
          const timestampKey = `${moment(rangeStart).format('T_HH:mm')}_Start`;
    
          dailyTimeRanges.push({
            [timestampKey]: startTimestamp,
            [`${moment(rangeEnd).format('T_HH:mm')}_End`]: endTimestamp,
          });
        }
      }
    
      updatedDaysArray.push({ [day]: dailyTimeRanges });
    }
    
    // Log the updated daysArray
    console.log('Updated daysArray: ', updatedDaysArray);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>


Solution

  • In your code you have about the following structure

     for (let i = 0; i < daysArrayDummy.length; i++) {
     ...
       for (let j = 0; j < durationOfCampaignInDays; j++) {
       ...
         for (let k = 0; k < numberOfTimeRanges; k++) {
           ...
           dailyTimeRanges.push(...);
         }
       }
       updatedDaysArray.push({ [day]: dailyTimeRanges });
     }
    

    In the outermost loop you iterate over the weekdays (Sunday ... Saturday). In the next level you iterate over the whole duration of your campaign (ie 8, 14 or whatever number of days). And in the innermost loop you iterate over the ranges within a day.

    Thus for every weekday (eg a Sunday) you will create dailyRanges for every day in your campaign duration, regardless of whether that day is really a Sunday or not

    How to fix this?

    First of all, I think your desired data structure seems a bit strange. You want an array where each element only has a single property which is named differently in each of them containing an array of ranges. A more intuitive data structure would be something like the following

    {
       Sundays: [...],
       Mondays: [...]
       ...
    }
    

    But that is not my call to make.

    The simplest solution to this is probably to check if the day you are currenlty looking at in your inner loop is really of the same weekday you are looking at in your outer loop and if not, skip it.

    for (let j = 0; j < durationOfCampaignInDays; j++) {
      const dayStart = moment(startOfTheDay).add(j, 'days').startOf("day");
      const weekday = dayStart.day(); // gives you the weekday as number, 0 = sun, 1 = mon, ...
    
      //if the weekday of the current day isn't the same as the weekday
      //in the outer loop, skip generating ranges
      if (weekday != i) continue;
    
      ...
    }
    

    The more efficient solution to get your desired result is probably

    1. Get rid of the outermost loop
    2. In the loop over the length of your campaing check what weekday the current date actually is and use this to determine, which day you should add the generated timeranges to

    Ie something like this

    //initialize the result with all weekdays and empty ranges array
    let weekdays = ["Sundays","Mondays", ..., "Saturdays"];
    let updatedDaysArray = weekdays.map(x => { [x]: [] });
    for (let j = 0; j < durationOfCampaignInDays; j++) {
      const 
        dayStart = moment(startOfTheDay).add(j, 'days').startOf("day"),
        weekday = dayStart.day(),  //gets the weekday 0 = sun, 1 = mon, ...
        weekdayname = weekdays[weekday];
    
      //retrieve the existing array of ranges from the result
      //and add the new ranges for that day to that array
      let dailyTimeRanges = updatedDaysArray[weekday][weekdayname];
      for (let k = 0; k < numberOfTimeRanges; k++) {
        ...
        dailyTimeRanges.push(...);
      }
    
    }
    

    And for the more intuitive data structure I suggested

    //initialize the result with all weekdays and empty ranges array
    let weekdays = ["Sundays","Mondays", ..., "Saturdays"];
    let result = Object.fromEntries(weekdays.map(x => [x, []));
    for (let j = 0; j < durationOfCampaignInDays; j++) {
      const 
        dayStart = moment(startOfTheDay).add(j, 'days').startOf("day"),
        weekday = dayStart.day(),  //gets the weekday 0 = sun, 1 = mon, ...
        weekdayname = weekdays[weekday];
    
      ...
      //retrieve the existing array of ranges from the result
      //and add the new ranges for that day to that array
      let dailyTimeRanges = result[weekdayname];
      for (let k = 0; k < numberOfTimeRanges; k++) {
        ...
        dailyTimeRanges.push(...);
      }
    
    }