Team calendar mapping to find common available open slots - javascript

I am using a calendar application that shows the available timeslots for a group. For example if the team has 2 members, and if each member has their own workingHours and own existing bookings; to show the team calendar where the array output is only giving all slots that are commonly (collectively) open among all the members. Code below is incorrectly providing returns that are not consistent with the common open slots.
userSchedule has the working hours and busy times of the 2 members of the team
userSchedule = [{"workingHours":[{"days":[1,2,3,4,5],"startTime":60,"endTime":540}],"busy":[]},{"workingHours":[{"days":[2,3],"startTime":60,"endTime":540},{"days":[5],"startTime":60,"endTime":450},{"days":[1],"startTime":180,"endTime":540}],"busy":[]}]
From this I want to get the working hours of the combined team when both are available first. Then i want to removed the blocked times and only show in team calendar (in an array) the actual days and slots that the combined team is available.
The below logic is not working and showing output where the workingHours are not all days that both are available
const workingHours = userSchedules?.reduce(
(currentValue: ValuesType<typeof userSchedules>["workingHours"], s) => {
// Collective needs to be exclusive of overlap throughout - others inclusive.
if (eventType.schedulingType === SchedulingType.COLLECTIVE) {
// taking the first item as a base
if (!currentValue.length) {
currentValue.push(...s.workingHours);
return currentValue;
}
// the remaining logic subtracts
return s.workingHours.reduce((compare, workingHour) => {
return compare.map((c) => {
const intersect = workingHour.days.filter((day) => c.days.includes(day));
return intersect.length
? {
days: intersect,
startTime: Math.max(workingHour.startTime, c.startTime),
endTime: Math.min(workingHour.endTime, c.endTime),
}
: c;
});
}, currentValue);
} else {
// flatMap for ROUND_ROBIN and individuals
currentValue.push(...s.workingHours);
}
return currentValue;
},
[]
);

Related

Get the last meeting date from Google Calendar using guest email address

I need a method in which I can scan through previous meetings (this can go months back) in Google Calendar and find the last meeting (Google Calendar event) with a person (email address) and the number of meetings I've had with them.
Searching in the documentation: https://developers.google.com/apps-script/reference/calendar
There is no method to find events by email. The result I would need returned is the date of last meeting and count of total meetings including that person.
Has anyone done anything like that? I would greatly appreciate your help!
You can specify a search query using getEvents(), then use getGuestByEmail() and getGuestStatus() to check if you met with them.
function getLastEventWith(contactEmail) {
const events = CalendarApp.getDefaultCalendar() // Doesn't have to be the default calendar
.getEvents(new Date(1970, 0, 1), new Date(), { search: contactEmail })
.reverse(); // Events are sorted earliest-to-latest, so reverse
let total = 0;
let lastEvent;
for (let event of events) {
const guest = event.getGuestByEmail(contactEmail);
if (guest.getGuestStatus() === CalendarApp.GuestStatus.YES || guest.getGuestStatus() === CalendarApp.GuestStatus.OWNER) {
if (total === 0) {
lastEvent = event;
}
total++;
}
}
return { lastEvent: lastEvent, total: total };
}
function test_getLastEventWith() {
const { lastEvent, total } = getLastEventWith("someone#example.com");
console.log(lastEvent.getTitle());
console.log(lastEvent.getStartTime());
console.log(total);
}
You may be able to do this with fewer calls by using the Calendar Advanced Service.

Iterate through collection items and sum property of each document while being subscribed for a collection

I have a collection counters, each counter document has a collection actions. Each action has a step property which is a Number.
Here's how I get all (subscribe) counters
firestoreRef.onSnapshot(
(countersSnapshot) => {
const arrayOfCounters = [];
if (!countersSnapshot.empty) {
countersSnapshot.forEach((counterDocument) =>
arrayOfCounters.push(counterDocument),
);
}
setCounters(arrayOfCounters); // <- this is React.useState()[1]
},
(e) => console.error(e),
);
I used to have actions' as an array in counter and I would get "currentValue" (which is a sum of all step values in actions) like this:
const getCurrentValue = (initialValue, actions, step) => {
if (!actions.length) {
return initialValue;
}
return actions.reduce((a, b) => {
return b.type === TYPE_INCREMENT ? a + step : a - step;
}, initialValue);
};
However I decided to transform actions to a Firestore collection. How should I get all counters and their current value, while also being subscribed to this so I get real-time updates?
Posting as Community Wiki answer, based in the comments.
Following examples here, should provide a good understanding and help with ideas to solve the issue. For example, subscribing to each counter.ref.collection('actions') might be a solution, however, take in account of all the queries will be made for each of the documents. I believe this link should help, as it's a similar cases and provide multiples alternatives - with code samples included - of solutions.

Vue computed property overwriting global state without vuex

I have a list of people who have scores. In state I have them listed in an array, one of the items in the array is 'scoreHistory' which is an array of objects containing their scores at different points in time. I want to filter this set for different time periods i.e. -5 days, -30 days so instead of just seeing the overall score I can see the scores if everyone started at 0 say 30 days ago.
I have it (kind of) working. See my code below:
filteredScores () {
if(!this.people) {
return
}
// Here I was trying to ensure there was a copy of the array created in order to not change the original array. I thought that might have been the problem.
let allPeople = this.people.slice(0) // this.people comes from another computed property with a simple getter. Returns an array.
let timeWindow = 30 //days
const windowStart = moment().subtract(timeWindow,'days').toDate()
for (const p of allPeople ) {
let filteredScores = inf.scoreHistory.filter(score => moment(score.date.toDate()).isSameOrAfter(windowStart,'day'))
p.scoreHistory=filteredScores
//calculate new score
p.score = inf.scoreHistory.reduce(function(sum,item) {
return sum + item.voteScore
},0)
}
return allInf
}
I expected it to return to me a new array where each person's score is summed up over the designated time period. It seems to do that OK. The problem is that it is altering the state that this.people reads from which is the overall data set. So once it filters all that data is gone. I don't know how I am altering global state without using vuex??
Any help is greatly appreciated.
Your problem isn't that you're modifying the array, but that you're modifying the objects within the array. You change the scoreHistory and score property of each item in the array. What you want to do instead is create a new array (I recommend using map) where each item is a copy of the existing item plus a new score property.
filteredScores () {
if(!this.people) {
return
}
let timeWindow = 30 //days
const windowStart = moment().subtract(timeWindow,'days').toDate()
return this.people.map(p => {
let filteredScores = p.scoreHistory.filter(score => moment(score.date.toDate()).isSameOrAfter(windowStart,'day'))
//calculate new score
let score = filteredScores.reduce(function(sum, item) {
return sum + item.voteScore
}, 0)
// Create a new object containing all the properties of p and adding score
return {
...p,
score
}
}
})

Checking for part of a key value pair

I am currently trying to develop an app that displays the parking tickets in NYC. The API I am using is https://data.cityofnewyork.us/resource/ati4-9cgt.json. I am trying to create a graph in which it shows how many tickets were distributed per month. There is a column called "issue_date", and it has year, month, followed by a bunch of numbers I am unsure of. Is it possible to only check the year and month part, instead of the whole value? For example, if I only want to see if the ticket was issued in January, I would check for 2017-01, but how would I make it so that the rest of the value doesn't get accounted and break the code? Thank you!
I forgot to include that I am a first year in college. I have limited knowledge on programming, sorry for the inconvenience.
You can just grab the first 7 characters from the date.
Here is some code to count the number of entries per month and put that in an array:
fetch("https://data.cityofnewyork.us/resource/ati4-9cgt.json")
.then(resp => resp.json()).then(data => {
const byMonth = Object.entries(data.reduce( (acc, row) => {
const month = row.issue_date.slice(0,7); // <--- just taking YYYY-MM
acc[month] = acc[month] || 0;
acc[month]++; // <--- counting
return acc;
}, {})).sort((a, b) => a[0].localeCompare(b[0])); // <--- sorting months
console.log(byMonth); // <--- output [[month, count], [month, count], ...]
});
Match issuedate with regexp, e.g. /^2017-01/
So if you have this whole array, you could:onlyOnJanuary2017 = wholeArray.filter(object => object.issue_date.match(/^2017-01/)); as here: https://jsfiddle.net/twpq6nvd/
Additionaly, if you want to bucket themm try this
var result = arr.reduce((accumulator, item) => {
const date = new Date(item.issue_date)
var key = `${date.getFullYear()}-${date.getMonth()}`
accumulator[key] = accumulator[key] || [];
accumulator[key].push(item);
return accumulator;
}, {})
https://jsfiddle.net/d3qa96bg/1/

immutable.js filter and mutate (remove) found entries

I have two loops, one for each day of the month, other with all events for this month. Let's say I have 100 000 events.
I'm looking for a way to remove events from the main events List once they were "consumed".
The code is something like:
const calendarRange = [{initialDate}, {initialDate}, {initialDate}, {initialDate}, ...] // say we have 30 dates, one for each day
const events = fromJS([{initialDate}, {initialDate}, {initialDate}, ...]) // let's say we have 100 000
calendarRange.map((day) => {
const dayEvents = events.filter((event) => day.get('initialDate').isSame(event.get('initialDate'), 'day')) // we get all events for each day
doSomeThingWithDays(dayEvents)
// how could I subtract `dayEvents` from `events` in a way
// the next celandarRange iteration we have less events to filter?
// the order of the first loop must be preserved (because it's from day 1 to day 3{01}])
}
With lodash I could just do something like:
calendarRange.map((day) => {
const dayEvents = events.filter((event) => day.get('initialDate').isSame(event.get('initialDate'), 'day')) // we get all events for each day
doSomeThingWithDays(dayEvents)
pullAllWith(events, dayEvents, (a, b) => a === b)
}
How to accomplish the same optimization with immutablejs? I'm not really expecting a solution for my way of iterating the list, but for a smart way of reducing the events List in a way it get smaller and smaller..
You can try a Map with events split into bins - based on your example, you bin based on dates - you can lookup a bin, process it as a batch and remove it O(1). Immutable maps are fairly inexpensive, and fare much better than iterating over lists. You can incur the cost of a one time binning, but amortize it over O(1) lookups.
Something like this perhaps:
eventbins = OrderedMap(events.groupBy(evt => evt.get('initialDate').dayOfYear() /* or whatever selector */))
function iter(list, bins) {
if(list.isEmpty())
return
day = list.first()
dayEvents = bins.get(day.dayOfYear())
doSomeThingWithDays(dayEvents)
iter(list.shift(), bins.delete(day))
}
iter(rangeOfDays, eventbins)
By remobing already processed elements you are not going to make anything faster. The cost of all filter operations will be halved on average, but constructing the new list in every iteration will cost you some cpu cycles so it is not going to be significantly faster (in a big O sense). Instead, you could build an index, for example an immutable map, based on the initialDate-s, making all the filter calls unnecessary.
const calendarRange = Immutable.Range(0, 10, 2).map(i => Immutable.fromJS({initialDate: i}));
const events = Immutable.Range(0, 20).map(i => Immutable.fromJS({initialDate: i%10, i:i}));
const index = events.groupBy(event => event.get('initialDate'));
calendarRange.forEach(day => {
const dayEvents = index.get(day.get('initialDate'));
doSomeThingWithDays(dayEvents);
});
function doSomeThingWithDays(data) {
console.log(data);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>

Categories

Resources