Эффективно создавать массив дат?

У меня есть коллекция availabilities. Внутри этой коллекции может быть несколько записей, основанных, например, на дате:

{
  "_id": "64b03ed794d87927a3066e13",
  "startDateTime": "2023-07-07T18:00:00.000Z",
  "endDateTime": "2023-07-12T15:00:00.000Z",
  "availabilityType": "blackout"
}
{
  "_id": "64b03eb094d87927a3066ddb",
  "startDateTime": "2023-07-03T18:00:00.000Z",
  "endDateTime": "2023-07-06T15:00:00.000Z",
  "availabilityType": "blackout"
}

Я пытаюсь найти лучший способ создать массив дат для месяца, и если есть «доступность» для некоторых дней, то вернуть это в массиве дат. Я думал о создании массива дней с помощью javascript, но тогда мне пришлось бы повторять каждый день, чтобы увидеть, есть ли какие-либо availabilities в этот день; это кажется очень неэффективным.

В идеале то, что я хочу построить, похоже на следующее:

[
  {
    "date": "2023-07-01",
    "availabilityType": "available"
  },
  {
    "date": "2023-07-02",
    "availabilityType": "available"
  },
  {
    "date": "2023-07-03",
    "availabilityType": "booked" // because there was an availability entry in the collection that is marked as booked
  },
  {
    "date": "2023-07-04",
    "availabilityType": "booked" // because there was an availability entry in the collection that is marked as booked
  },
  {
    "date": "2023-07-05",
    "availabilityType": "booked" // because there was an availability entry in the collection that is marked as booked
  },
  {
    "date": "2023-07-06",
    "availabilityType": "booked" // because there was an availability entry in the collection that is marked as booked
  },
  ... etc up to July 31
]

Любые предложения о том, как это можно сделать?

🤔 А знаете ли вы, что...
JavaScript поддерживает работу с графикой и аудио, что позволяет создавать мультимедийные веб-приложения.


2
50
3

Ответы:

Один из подходов заключается в предварительном создании массива для месяца со статусом по умолчанию "available" для всех дней, а затем циклическим просмотром доступности и обновлением соответствующих записей в вашем массиве. например, сначала сгенерируйте массив за месяц:

const getMonth = (year, month) => {
    const start = new Date(year, month, 1);
    const end = new Date(year, month + 1, 1);
    const days = [];
    
    for(let day = start; day < end; day.setDate(day.getDate() + 1)) {
        days.push({
            date: day.toISOString().slice(0, 10),
            availabilityType: 'available',
        });
    }
    
    return days;
};

const monthData = getMonth(2023, 6); // for July 2023 (month is 0-indexed)

Затем обновите данные о доступности данными массива:

const availabilities = [
    { startDateTime: "2023-07-07T18:00:00.000Z", endDateTime: "2023-07-12T15:00:00.000Z", availabilityType: "blackout" },
    { startDateTime: "2023-07-03T18:00:00.000Z", endDateTime: "2023-07-06T15:00:00.000Z", availabilityType: "blackout" }
];

for (let av of availabilities) {
    const startDay = new Date(av.startDateTime).getDate();
    const endDay = new Date(av.endDateTime).getDate();
    
    for (let i = startDay - 1; i < endDay; i++) {
        monthData[i].availabilityType = av.availabilityType;
    }
}

Это предполагает, что availabilities отсортировано в порядке возрастания и не содержит перекрывающихся интервалов. Если эти предположения неверны, вам может потребоваться сначала отсортировать availabilities и добавить дополнительные проверки во втором цикле.


Вот еще один взгляд на это:

const bookings=[{ "_id": "64b03ed794d87927a3066e13", "startDateTime": "2023-07-07T18:00:00.000Z",
  "endDateTime": "2023-07-12T15:00:00.000Z", "availabilityType": "blackout"},
 {"_id": "64b03eb094d87927a3066ddb", "startDateTime": "2023-07-03T18:00:00.000Z",
  "endDateTime": "2023-07-06T15:00:00.000Z", "availabilityType": "blackout" }];

 const monthCal = {}; // set up the calendar for one month: July 2023   
 for (let d=1, m=6, y=2023, ld=new Date(y,m+1,0).getDate(); // set to 00:00h the following day
      d <= ld; d++ ) monthCal[`${y}-${String(m+1).padStart(2,"0")}-${String(d).padStart(2,"0")}`]=false;
      
bookings.forEach(b=>{
 // define the cut-off date ld as "00:00h", the day after endDateTime:
 const ld=new Date(b.endDateTime); ld.setDate(ld.getDate()+1); ld.setHours(0);
 for (let d=new Date(b.startDateTime); d < ld; d.setDate(d.getDate()+1)) 
  monthCal[d.toISOString().slice(0,10)]=true;
});

console.info(monthCal)

// or if you want the original output format:

console.info(Object.entries(monthCal).map(([date,v])=>({date,availabilityType:v?"booked":"available"})));

Поскольку объект monthCal позволяет находить даты напрямую, без использования внутреннего цикла, это может быть немного более эффективным, чем сканирование массива объектов.


Решено

Вы можете просто добиться этого, определив startDate и endDate для представления диапазона дат месяца, для которого вы хотите получить подробности. В демонстрационных целях я использую диапазон месяцев июля.

const startDate = new Date("2023-07-01");
const endDate = new Date("2023-07-31");

Затем мы будем перебирать каждую дату, используя цикл for, начиная с startDate и увеличивая на один день до endDate.

В цикле мы проверяем, попадает ли текущая дата в диапазон startDateTime и endDateTime любого объекта в массиве с типом доступности blackout. Если это так, переменная isBooked устанавливается на true, указывая, что дата забронирована. В противном случае isBooked остается false, указывая на то, что дата свободна.

Наконец, мы помещаем объект в массив allDates для каждой даты, устанавливая свойство даты в отформатированную строку даты, а доступное свойство — либо в booked, либо в available в зависимости от значения isBooked.

Живая демонстрация:

const array = [
  {
    "_id": "64b03ed794d87927a3066e13",
    "startDateTime": "2023-07-07T18:00:00.000Z",
    "endDateTime": "2023-07-12T15:00:00.000Z",
    "availabilityType": "blackout"
  },
  {
    "_id": "64b03eb094d87927a3066ddb",
    "startDateTime": "2023-07-03T18:00:00.000Z",
    "endDateTime": "2023-07-06T15:00:00.000Z",
    "availabilityType": "blackout"
  }
];

const startDate = new Date("2023-07-01");
const endDate = new Date("2023-07-31");

const allDates = [];
for (let date = startDate; date <= endDate; date.setDate(date.getDate() + 1)) {
  const dateString = date.toISOString().split('T')[0];
  const isBooked = array.some(item => {
    const startDateTime = new Date(item.startDateTime);
    const endDateTime = new Date(item.endDateTime);
    return dateString >= startDateTime.toISOString().split('T')[0] && dateString <= endDateTime.toISOString().split('T')[0] && item.availabilityType === "blackout";
  });
  allDates.push({ date: dateString, available: isBooked ? "booked" : "available" });
}

console.info(allDates);