I have a table for a month with a row for each day of the table and an array with names. For every day there should be picked a random name from the array while every name should be picked about the same amount of times if possible.
The following function checks if every user in the array is available for the certain day and excludes the name if not.
function generateRandomSchedule(month) {
// some more code including variable declaration here
for(let i=1; i<daysInMonth+1; i++) {
let date = `${moment(i, 'D').format('DD')}.${monthNumber}.`;
// filter required keys
const keys = Object.keys(localStorage).filter(key => key.includes(date) && !key.startsWith(month));
let excludeUsers = [];
keys.forEach((key) => {
let keyValue = localStorage.getItem(key);
// check if user is available for the certain day
if(key.includes('Urlaub') && dutyUserArray.includes(keyValue)) {
excludeUsers.push(keyValue);
}
if(key.includes('Spaet') && dutyUserArray.includes(keyValue)) {
excludeUsers.push(keyValue);
}
})
// remove unavailable users from array
let updatedArray = dutyUserArray.filter(user => !excludeUsers.includes(user));
// get random user from the available users
let randomName = updatedArray[Math.floor(Math.random() * updatedArray.length)];
// some more code to add randomName to table here
}
}
This code works fine so far, just that it's picking random array elements unequally, e.g. name1 has been added 6 times to the table while name2 just once and name3 4 times.
1 Answer 1
I would first determine the user with the minimum number of days workable, and the number of users in the pool. If minDays*poolSize <= monthSize, then duplicate each user minDays times into a list, then add monthSize - minDays*poolSize "dummy" users to make the list match the number of days in the month, otherwise evenly duplicate round-robin style until you match the day count. Perform a "stable marriage" match (See Gale-Shapley algorithm) between the two lists of duped users to days.
Create a new list of all users omitting those matching the prior minimum number of days workable, and repeat the above process for that set of users against any unmatched or dummy-matched days.
Continue doing so until all days are matched. It's possible at this point that the schedule is unsolvable. Trivially, you could have a pool of 1 users with 1 day available in a month of 30 days.
Once solved, iterate the matched user-days, performing random swaps between user-days where permitted by your dutyUserArray.
updatedArrayby that quantity, and choose randomly from names matching the lowest quantity.