0

I'm trying to get the list of k closest dates from a day of the week but I don't know how. Can someone please give me a hint or suggestion? Thank you.

For example:

getDates(k, day) { .... } // k is number of dates (1,2,3 etc), day is day of the week ("monday", "tuesday" etc)

// today is 05/19/2020
getDates(1, "monday") // output: [05/25/2020]
getDates(2, "monday") // output: [05/25/2020, 06/01/2020]
Louis Tran
  • 1,154
  • 1
  • 26
  • 46
  • Is there anything you've done so far ? – Cid May 19 '20 at 05:55
  • Get the date today. Add one day in a loop until [Date.getDay](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) returns the respective value for 'day'. Keep adding 7 days for each loop of k (-1, since the first date is already found). – user2864740 May 19 '20 at 05:57
  • @user2864740 That's a great idea. Thank you. – Louis Tran May 19 '20 at 05:58
  • @user2864740 Quick question: How come the first date is already found? – Louis Tran May 19 '20 at 05:59
  • I don't know how from `getDates(2, "monday")` you get `[05/25/2020, 06/01/2020]`. What the second argument should do ? – Cid May 19 '20 at 06:00
  • 1
    Say today was a Sunday, then the first loop to sync-to-"monday" (today) would end when the date at the end of the loop was .. already Monday. So that accounts for "1" k already: only k-1 more loops for the full weeks need to be done. – user2864740 May 19 '20 at 06:00
  • @user2864740 It passed 12:00. Today is Tuesday. – Louis Tran May 19 '20 at 06:01
  • 1
    @LouisTran Ahaha, not here just yet ;-) Anyway, same would be the current date was Wednesday. The first loop to sync/advance-to "Monday" would consume one k. – user2864740 May 19 '20 at 06:02
  • 1
    It could actually be done entirely with one loop.. that'd be simpler.. just keep adding days until repeatedly hitting "Monday" k times. :| Obviously this approach isn't so great for computing "Monday" a billion years in the future. For dates within a few months: no problem. – user2864740 May 19 '20 at 06:03
  • @user2864740 for my problem. k is limited in range (1,10) so I think it fits perfectly. – Louis Tran May 19 '20 at 06:06

2 Answers2

0

I'm convinced that you have already figured out how to solve this question from all the comments. So I'll just post my answer to give my personal approach to this problem.

I'm pretty sure that it's very difficult to solve this without using:

1: day to number converter (sunday => 0, monday => 1, and so on... This is because Date.prototype.getDay returns a number, not in the word form)

and

2: date addition function (because there are couple of date addition that we can't avoid (one for deciding the nearest day of the week, and another for creating new dates for the output array), it's better to just make it a function.)

My approach is (although not fully optimized):

1: First, convert the input day to integer form. This can be done in few ways, but I simply created an array of dates, and used the indexOf method to get the day number. But there is another neat way, which is flexible to any region you live (application of this SOF link).

2: Then, get the nearest date from the current time with day day (the while loop in the middle). I think it's very easy to read, but we can use a one liner here. Which will look something like new_day.setDate(new_day.getDate() + (day_num - first_day.getDay() + (7 * (new_day.getDay() > day_num))));. But I personally feel this is a bit nasty, so I prefer the loop method.

3: Finally, create the array of dates. In my code, I first created the empty array of k items (without fill(), the array will be [empty, empty...] and will be not iterable), then map them to corresponding dates from calculation.

There is actually a downfall to my method though. When I'm converting the date object to the form MM/DD/YYYY, i'm using the toLocaleDateString method. But apparently, using toLocaleDateString() is risky (see the comment to this SOF answer), which I'm not really sure why, but if so that would be a problem.

Also, the output is 5/25/2020 while your expected output is 05/25/2020. It depends on your expectation, but it might be a problem.

function addDays(date, day) {
    let new_date = new Date(date.getTime());
    new_date.setDate(new_date.getDate() + day);
    return new_date;
}

function getDates(k, day) {
    const day_num = ['sunday', 'monday', 'tuesday', 'wednesday' ,'thursday' ,'friday', 'saturday'].indexOf(day.toLowerCase()) // converting day name to number

    let new_day = new Date();
    while (new_day.getDay() !== day_num) {
        new_day = addDays(new_day, 1)
    }
    
    return Array(k).fill().map((_, index) => addDays(new_day, index * 7).toLocaleDateString() )
}

// today is 05/19/2020
console.log(getDates(1, "monday")) // output: [05/25/2020]
console.log(getDates(2, "monday")) // output: [05/25/2020, 06/01/2020]
console.log(getDates(10, "monday"))
console.log(getDates(20, "monday")) // also for k > 10

Hope that helped, cheers! :)

Kenta Nomoto
  • 839
  • 6
  • 11
0

As comments suggest, you can move the supplied date to the next instance of the required day, then keep adding 7 days until you get the number of dates you want.

In the following function, if start day is say Monday and the current day is also Monday, then the first date in the results array is the current day. If you want it to be the following week, change:

dateDay > idx?

to

dateDay >= idx?

I think the function should return an array of Dates, then the caller can format them as required.

/**
 * Get array of count date stings from next dayName starting on or after today or d
 * @param {number} count: number of dates to get (1,2,3 etc.)
 * @param {string} dayName: name of day of week, min first 2 letters, any case ("Mon", "TU","we" etc)
 * @param {Date} [d]: starting date
 * @returns {string[]} Array of k date strings starting with next day, each +7 days from the previous date
*/
function getDates(count, dayName, d = new Date()) {
  // Don't modify supplied date
  d = new Date(+d);
  // Get supplied day name as JS day index
  let idx = ['su','mo','tu','we','th','fr','sa'].indexOf(dayName.toLowerCase().slice(0,2));
  // Get index of d's day
  let dayNum = d.getDay();
  // Set to next instance of day, or today if current day == day
  d.setDate(d.getDate() + idx - dayNum + (dayNum > idx? 7 : 0));
  // Fill result array with required date strings
  let result = [];
  while (count-- > 0) result.push(d.toDateString()) && d.setDate(d.getDate() + 7);
  return result;
}

// Examples
[[1, "Monday"],
 [2, "TUES"],
 [3, 'wed'],
 [1, 'th'],
 [3, 'fr'],
 [2, 'sa'],
 [1, 'su'],
 [-20, 'fri'] // -ve count returns empty array
].forEach(args => console.log(getDates(...args)));
RobG
  • 142,382
  • 31
  • 172
  • 209