Home > OS >  How do I filter an array of objects base on two date/time and assign it base on the index of an arra
How do I filter an array of objects base on two date/time and assign it base on the index of an arra

Time:10-10

I have a scheduling system which should filter out user that has an appointment on that day.

The appointment has 3 types of timings;

  1. Full day appointment 0800-1900
  2. Half day appointment (AM/PM) AM (0800-1200) available at (1200-1900) / PM (1200-1900) available at (0800-1200)
  3. Custom timing appointment (user define the timing of the appointment from (0800-1900)

I manage to find a solution to solve point 1 and 2 however I am having issue for point 3.

I was thinking to assign user who does not have appointments to all the index of the array where each index indicates the row of the time slots i.e below.

  1. 0800-0900.. [index 0]
  2. 0900-1000.. [index 1]
  3. 1000-1100.. [index 2]
  4. ...
  5. 1800-1900.. [index 10]

My failed solution

import moment from 'moment';

// time index 
// [0800-0900] 0
// [0900-1000] 1
// [1000-1100] 2
// [1100-1200] 3
// [1200-1300] 4
// [1300-1400] 5
// [1400-1500] 6
// [1500-1600] 7
// [1600-1700] 8
// [1700-1800] 9
// [1800-1900] 10

const appointments = [
  // filter out from all the index 0-11
  {
    id: 1,
    isFullDay: true,
    startDate: new Date(2022, 10, 8, 0, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 23, 59, 59, 999),
    userId: 1
  },
  // filter out from index 6
  {
    id: 2,
    isFullDay: false,
    startDate: new Date(2022, 10, 8, 14, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 15, 0, 0, 0),
    userId: 2
  },
  // filter out from index 4-10
  {
    id: 3,
    isFullDay: false,
    startDate: new Date(2022, 10, 8, 0, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 12, 0, 0, 0),
    userId: 3
  }
]

const users = [
  {
    id: 1,
    shortName: "PHILIP"
  },
  {
    id: 2,
    shortName: "SHARKY"
  },
  {
    id: 3,
    shortName: "JOHN"
  },
  {
    id: 4,
    shortName: "YUSOF"
  }
]

// [id, id, id]
// const ids = appointments.map(({ userId }) => userId);
const usersHaveFullDayAppointments = appointments.filter(app => app.isFullDay === true).map(app => app.userId);
const userHaveHalfDayAMAppointments = appointments.filter(app => app.isFullDay !== true).filter(app => moment(app.startDate).hour() === 0 && moment(app.endDate).hour() === 12).map(app => app.userId);
const userHaveHalfDayPMAppointments = appointments.filter(app => app.isFullDay !== true).filter(app => moment(app.startDate).hour() === 12 && moment(app.endDate).hour() === 23).map(app => app.userId);

const slots = new Array(11).fill(null).map((_, index) => {
  const slot = [];
  if (index <= 3) {
    slot.push(...users.filter(({ id }) => userHaveHalfDayAMAppointments.includes(id)))
  } else {
    slot.push(...users.filter(({ id }) => userHaveHalfDayPMAppointments.includes(id)))
  }

  slot.push(...users.filter(({ id }) => !usersHaveFullDayAppointments.includes(id)).filter(({ id }) => !userHaveHalfDayPMAppointments.includes(id)).filter(({ id }) => !userHaveHalfDayAMAppointments.includes(id)))

  return slot;
})

console.log(slots, "time");

Test result


[
  [
    {
      "id": 3,
      "shortName": "JOHN"
    },
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 3,
      "shortName": "JOHN"
    },
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 3,
      "shortName": "JOHN"
    },
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 3,
      "shortName": "JOHN"
    },
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ]
]

Expected result

[
  [
    {
      "id": 3,
      "shortName": "JOHN"
    },
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 3,
      "shortName": "JOHN"
    },
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 3,
      "shortName": "JOHN"
    },
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 3,
      "shortName": "JOHN"
    },
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ],
  [
    {
      "id": 2,
      "shortName": "SHARKY"
    },
    {
      "id": 4,
      "shortName": "YUSOF"
    }
  ]
]

Share code playground here https://playcode.io/980946

CodePudding user response:

EDITED.

Append "slots2" lines, please.

I just flipped the result.

Use the array "slots2" instead of "slots".

.


.

OK. Please append "userHaveCustomTimingAppointments" after the "userHaveHalfDayPMAppointments" line.

And then add two "slot.push" lines for FullDay and CustomTiming.

Finally just comment out the last slot.push line.

import moment from 'moment';

// time index 
// [0800-0900] 0
// [0900-1000] 1
// [1000-1100] 2
// [1100-1200] 3
// [1200-1300] 4
// [1300-1400] 5
// [1400-1500] 6
// [1500-1600] 7
// [1600-1700] 8
// [1700-1800] 9
// [1800-1900] 10

const appointments = [
  // filter out from all the index 0-11
  {
    id: 1,
    isFullDay: true,
    startDate: new Date(2022, 10, 8, 0, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 23, 59, 59, 999),
    userId: 1
  },
  // filter out from index 6
  {
    id: 2,
    isFullDay: false,
    startDate: new Date(2022, 10, 8, 14, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 15, 0, 0, 0),
    userId: 2
  },
  // filter out from index 4-10
  {
    id: 3,
    isFullDay: false,
    startDate: new Date(2022, 10, 8, 0, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 12, 0, 0, 0),
    userId: 3
  }
]

const users = [
  {
    id: 1,
    shortName: "PHILIP"
  },
  {
    id: 2,
    shortName: "SHARKY"
  },
  {
    id: 3,
    shortName: "JOHN"
  },
  {
    id: 4,
    shortName: "YUSOF"
  }
]

// [id, id, id]
// const ids = appointments.map(({ userId }) => userId);
const usersHaveFullDayAppointments = appointments.filter(app => app.isFullDay === true).map(app => app.userId);
const userHaveHalfDayAMAppointments = appointments.filter(app => app.isFullDay !== true).filter(app => moment(app.startDate).hour() === 0 && moment(app.endDate).hour() === 12).map(app => app.userId);
const userHaveHalfDayPMAppointments = appointments.filter(app => app.isFullDay !== true).filter(app => moment(app.startDate).hour() === 12 && moment(app.endDate).hour() === 23).map(app => app.userId);

// *** APPEND CODE (1) START ***
const userHaveCustomTimingAppointments = appointments
  .filter(a => usersHaveFullDayAppointments.indexOf(a.userId) < 0)
  .filter(a => userHaveHalfDayAMAppointments.indexOf(a.userId) < 0)
  .filter(a => userHaveHalfDayPMAppointments.indexOf(a.userId) < 0)
  .map(app => [app.userId, moment(app.startDate).hour(), moment(app.endDate).hour()]);
// *** APPEND CODE (1) END ***

const slots = new Array(11).fill(null).map((_, index) => {
  const slot = [];
  if (index <= 3) {
    slot.push(...users.filter(({ id }) => userHaveHalfDayAMAppointments.includes(id)))
  } else {
    slot.push(...users.filter(({ id }) => userHaveHalfDayPMAppointments.includes(id)))
  }

  // *** APPEND CODE (2) START ***
  slot.push(    // FullDay
    ...users.filter(({ id }) => usersHaveFullDayAppointments.includes(id))
  );
  slot.push(    // CustomTiming
    ...users.filter(({ id }) => {  // p is the position of userHaveCustomTimingAppointments array
      const p = userHaveCustomTimingAppointments.map(a => a[0]).indexOf(id);
      if(p >= 0){  // if the user exists, check the slot's hour with from-to value.
        const from = userHaveCustomTimingAppointments[p][1];
        const to   = userHaveCustomTimingAppointments[p][2];
        if(index   8 >= from && index   8 < to){
          return true
        }  // if the index between from and to, add the user to its slot.
      }  // magic number 8 comes from the "time index" table's first row.
    })  // because time index 0 means from "8" to 9.
  );
  // *** APPEND CODE (2) END ***

  // *** COMMENT OUT THE LINE BELOW ***
  //  slot.push(...users.filter(({ id }) => !usersHaveFullDayAppointments.includes(id)).filter(({ id }) => !userHaveHalfDayPMAppointments.includes(id)).filter(({ id }) => !userHaveHalfDayAMAppointments.includes(id)))

  return slot;
})
    
//    console.log(slots, "time");

// *** APPEND CODE (3) START ***
const slots2 = [];
slots.forEach((a, i) => {
  slots2.push(users.filter((b) => !a.map((c) => c.id).includes(b.id)));
});                                                 // flip the result.
console.log(slots2, 'time');
// *** APPEND CODE (3) END ***

Online Demo: https://stackblitz.com/edit/typescript-s1bl7d?devToolsHeight=33&file=index.ts

Result:

0: Array[2]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 4
        shortName: "YUSOF"
1: Array[2]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 4
        shortName: "YUSOF"
2: Array[2]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 4
        shortName: "YUSOF"
3: Array[2]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 4
        shortName: "YUSOF"
4: Array[3]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 3
        shortName: "JOHN"
    2: Object
        id: 4
        shortName: "YUSOF"
5: Array[3]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 3
        shortName: "JOHN"
    2: Object
        id: 4
        shortName: "YUSOF"
6: Array[2]
    0: Object
        id: 3
        shortName: "JOHN"
    1: Object
        id: 4
        shortName: "YUSOF"
7: Array[3]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 3
        shortName: "JOHN"
    2: Object
        id: 4
        shortName: "YUSOF"
8: Array[3]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 3
        shortName: "JOHN"
    2: Object
        id: 4
        shortName: "YUSOF"
9: Array[3]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 3
        shortName: "JOHN"
    2: Object
        id: 4
        shortName: "YUSOF"
10: Array[3]
    0: Object
        id: 2
        shortName: "SHARKY"
    1: Object
        id: 3
        shortName: "JOHN"
    2: Object
        id: 4
        shortName: "YUSOF"
time_index from to PHILIP SHARKY JOHN YUSOF
0 8 9 0 1 0 1
1 9 10 0 1 0 1
2 10 11 0 1 0 1
3 11 12 0 1 0 1
4 12 13 0 1 1 1
5 13 14 0 1 1 1
6 14 15 0 0 1 1
7 15 16 0 1 1 1
8 16 17 0 1 1 1
9 17 18 0 1 1 1
10 18 19 0 1 1 1

CodePudding user response:

I think I understand that the OP aims to group appointments by hour, producing something like:

{ hour: [ user names... ], hour: [ user names... ], ...

A good start is a predicate that answers the question: is a given appointment happening during a given hour? Another way to state it: does the hour fall between the start and end times?

// appointment is the OP's appointment object with start and end js date objects
// date is a js date object and hour is an int 8-19
function happensOn(appointment, date, hour) {
  // we could probably do all this natively, without moment, but the OP uses it...
  const start = moment(appointment.startDate);
  const end = moment(appointment.endDate);
  const theHour = moment(date).set('hour', hour);
  return theHour.isBetween(start, end, 'hour', '[]');  // means inclusive
}

Notice this answers the question for any given day and hour, for any appointment, regardless of the isFullDay flag, which can be treated as superfluous for this question.

Presuming that the user array might be large, let's build an index that allows a quick lookup of user, given an id:

// produces { id: user, id: user, ... }
const userIndex = users.reduce((index, user) => {
  index[user.id] = user;
  return index;
}, {});

That's really all that we need to do the grouping. As long as the appointment array is small (-ish, in the hundreds of elements or fewer), we can just walk through 11 hours and find the matching appointments.

// produce an object like { hour: [ user names ], hour: [ user names ], ...
let hours = [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
let today = new Date();
let schedule = hours.reduce((sch, hour) => {
  let appts = appointments.filter(app => happensOn(app, today, hour));
  let users = appts.map(app => userIndex[app.userId].shortName);
  sch[hour] = users;
  return sch;
}, {});

Here's a demo...

const appointments = [
  // Philip should appear at every hour
  {
    id: 1,
    isFullDay: true,
    startDate: new Date(2022, 10, 8, 0, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 23, 59, 59, 999),
    userId: 1
  },
  // Sharky should appear at hours 14, 15
  {
    id: 2,
    isFullDay: false,
    startDate: new Date(2022, 10, 8, 14, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 15, 0, 0, 0),
    userId: 2
  },
  // John should appear in the morning, till noon
  {
    id: 3,
    isFullDay: false,
    startDate: new Date(2022, 10, 8, 0, 0, 0, 0),
    endDate: new Date(2022, 10, 8, 12, 0, 0, 0),
    userId: 3
  }
]

const users = [
  {
    id: 1,
    shortName: "PHILIP"
  },
  {
    id: 2,
    shortName: "SHARKY"
  },
  {
    id: 3,
    shortName: "JOHN"
  },
  {
    id: 4,
    shortName: "YUSOF"
  }
]

// appointment is the OP's appointment object with start and end js date objects
// date is a js date object and hour is an int 8-19
function happensOn(appointment, date, hour) {
  // we could probably do all this natively, without moment, but the OP uses it...
  const start = moment(appointment.startDate);
  const end = moment(appointment.endDate);
  const theHour = moment(date).set('hour', hour);
  return theHour.isBetween(start, end, 'hour', '[]'); // means inclusive
}

// produces { id: user, id: user, ... }
const userIndex = users.reduce((index, user) => {
  index[user.id] = user;
  return index;
}, {});

// produce an object like { hour: [ users ], hour: [ users ], ...
let hours = [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
let today =  new Date(2022, 10, 8, 0, 0, 0, 0);
let schedule = hours.reduce((sch, hour) => {
  let appts = appointments.filter(app => happensOn(app, today, hour));
  let users = appts.map(app => userIndex[app.userId].shortName);
  sch[hour] = users;
  return sch;
}, {});

console.log(schedule)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>

  • Related