Filtering events by date in Swift4 - arrays

I'm building an application that provides autistic children with a very simple visual calendar based on scheduled events. So far, I've had no major issues and my events are filtering correctly when the first one is added for each recurrence frequency (daily, weekly, monthly, yearly).
However, for some reason, when I attempt to add a second event with a recurrence frequency that matches any events previously added, it will not populate.
I have attempted to debug and narrow down the issue myself to no avail. I'm sure I'm missing something extremely simple. Thank you in advance for any assistance tracking this down.
This function filters the entire list of dates by date:
func filterEvents() {
filteredEvents = []
tempEvents = []
dates = []
removeOldEvents()
generateRecurrances()
extractDates()
var currentIndex = 0
for date in dates {
if filteredEvents.count == 0 {
filteredEvents.append([])
}
else if filteredEvents.count != currentIndex + 1 {
filteredEvents.append([])
}
filteredEvents[currentIndex] = tempEvents.filter({ $0.filterDate() == date })
currentIndex += 1
}
}
This function is used to extract the necessary information to filter by date:
func extractDates() {
for event in tempEvents {
let extractedDate: (month: Int, day: Int, year: Int) = (event.month(), event.day(), event.year())
dates.append(extractedDate)
}
var currentIndex = 0
for date in dates {
if currentIndex != 0 {
if date.month == dates[currentIndex - 1].month && date.day == dates[currentIndex - 1].day && date.year == dates[currentIndex - 1].year {
dates.remove(at: currentIndex)
currentIndex -= 1
}
}
currentIndex += 1
}
}
This function generates recurring future events based on the user's preference when creating the original event:
func generateRecurrances() {
var dateComponent = DateComponents()
var daysToAdd = 0
var weeksToAdd = 0
var monthsToAdd = 0
var yearsToAdd = 0
for event in events {
if event.recurrenceFrequency == 1 {
for _ in 1...60 {
dateComponent.day = daysToAdd
dateComponent.month = 0
dateComponent.year = 0
let newDate = Calendar.current.date(byAdding: dateComponent, to: event.date)
tempEvents.append(Event(name: event.name, date: newDate!, image: event.image, completion: event.requiresCompletion, recurrenceFrequency: event.recurrenceFrequency))
daysToAdd += 1
}
}
else if event.recurrenceFrequency == 2 {
for _ in 1...8 {
dateComponent.day = weeksToAdd
dateComponent.month = 0
dateComponent.year = 0
let newDate = Calendar.current.date(byAdding: dateComponent, to: event.date)
tempEvents.append(Event(name: event.name, date: newDate!, image: event.image, completion: event.requiresCompletion, recurrenceFrequency: event.recurrenceFrequency))
weeksToAdd += 7
}
}
else if event.recurrenceFrequency == 3 {
for _ in 1...2 {
dateComponent.day = 0
dateComponent.month = monthsToAdd
dateComponent.year = 0
let newDate = Calendar.current.date(byAdding: dateComponent, to: event.date)
tempEvents.append(Event(name: event.name, date: newDate!, image: event.image, completion: event.requiresCompletion, recurrenceFrequency: event.recurrenceFrequency))
monthsToAdd += 1
}
}
else if event.recurrenceFrequency == 4 {
for _ in 1...2 {
dateComponent.day = 0
dateComponent.month = 0
dateComponent.year = yearsToAdd
let newDate = Calendar.current.date(byAdding: dateComponent, to: event.date)
tempEvents.append(Event(name: event.name, date: newDate!, image: event.image, completion: event.requiresCompletion, recurrenceFrequency: event.recurrenceFrequency))
yearsToAdd += 1
}
}
else if event.recurrenceFrequency == 0 {
tempEvents.append(event)
}
}
daysToAdd = 0
}
I really don't think this is related but, just for safety, here is the function that is used to automatically remove events with a date prior to the current date:
func removeOldEvents() {
var currentIndex = 0
let now = Event(name: "Now", date: Date(), image: #imageLiteral(resourceName: "Logo"), completion: false, recurrenceFrequency: 0)
for event in events {
if event.year() < now.year() {
events.remove(at: currentIndex)
currentIndex -= 1
}
else if event.month() < now.month() {
events.remove(at: currentIndex)
currentIndex -= 1
}
else if event.day() < now.day() {
events.remove(at: currentIndex)
currentIndex -= 1
}
currentIndex += 1
}
}

This modification to extractDates() has resolved this issue. Thank you for pointing me in the right direction.
func extractDates() {
for event in tempEvents {
dates.append(event.date)
}
var currentIndex = 0
for date in dates {
let newDate = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: date)!
dates[currentIndex] = newDate
print(dates[currentIndex])
currentIndex += 1
}
var tempDates = dates.removingDuplicates()
tempDates = tempDates.sorted()
dates = tempDates
for date in dates {
let calendar = Calendar.current
let year = calendar.component(.year, from: date)
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
dateComponents.append((month, day, year))
}
}

Related

Block specific time intervals or days on the syncfusion Scheduler

I am using the syncfusion to do the Scheduler calendar in the react code to block specific time intervals or days. Below the code is worked to block specific time intervals or days following by Subject, Doctor,StartTime, EndTime,RecurrenceRule and IsBlock in the return value, But this method just can follow each Doctor to block the date or time.
May I know how to block all doctors following by Subject, Doctor(This one how can choose all the doctor together),StartTime, EndTime,RecurrenceRule and IsBlock in my existing code? I set all doctor's values as dash -.
Below is my existing code to block specific time intervals or days following by Subject, Doctor,StartTime, EndTime,RecurrenceRule and IsBlock in the return value:
const getOrganizer = useCallback(() => {
getSchedulerOrganizerList().then((response) => {
var data = response.data;
setOrganizerList(
data.map((schedulerBlock) => {
var startDate = new Date(
formatDateStringAsDate2(schedulerBlock.startDate)
);
var endDate = new Date(
formatDateStringAsDate2(schedulerBlock.stopDate)
);
var count = null;
var repeatType = schedulerBlock.repeatType.toUpperCase();
if (repeatType === "DAILY") {
var Difference_In_Days = getDayDiff(startDate, endDate) + 1;
count = ";COUNT=" + Difference_In_Days;
} else if (repeatType === "WEEKLY") {
var Difference_In_Weeks = getWeekDiff(startDate, endDate) + 1;
count = ";COUNT=" + Difference_In_Weeks;
} else if (repeatType === "MONTHLY") {
var Difference_In_Months = getMonthDiff(startDate, endDate) + 1;
count = ";COUNT=" + Difference_In_Months;
} else if (repeatType === "YEARLY") {
var Difference_In_Years = getYearDiff(startDate, endDate) + 1;
count = ";COUNT=" + Difference_In_Years;
} else if (repeatType === "ONE TIME") {
repeatType = "ONE TIME";
}
var formatStopDate = moment(schedulerBlock.stopDate).format("YYYY-MM-DD");
var formatStopTime = moment(schedulerBlock.stopTime).format("HH:mm:ss");
var stopTimeValue = formatStopDate + "T" + formatStopTime;
var recurrenceRule = repeatType === "ONE TIME" ? null : "FREQ=" + repeatType + count;
var endTimeValue = repeatType === "ONE TIME" ? formatDateTimeStringAsDateTime(stopTimeValue) : formatDateTimeStringAsDateTime(schedulerBlock.stopTime);
return {
Id: schedulerBlock.id,
Subject: schedulerBlock.reason,
Doctor: schedulerBlock.doctor,
StartTime: formatDateTimeStringAsDateTime(schedulerBlock.startDate),
EndTime: endTimeValue,
RecurrenceRule: recurrenceRule,
IsBlock: true,
};
})
);
});
}, []);
Below is my sample return value for Dr X:
{
"Id": 19,
"Subject": "Test",
"Doctor": "Dr X",
"StartTime": "15 Aug 2022 09:00 PM",
"EndTime": "15 Aug 2022 11:00 PM",
"RecurrenceRule": "FREQ=WEEKLY;COUNT=8",
"IsBlock": true
}
Below are my current code results, in the calendar view I have 3 doctors which are Dr X, Dr Y, and Dr Z. I have blocked the Dr X at 9PM - 11 PM (August 15, 2022).
Now I am trying to do to block all doctors following by Subject, Doctor(This one how can choose all the doctor together),StartTime, EndTime,RecurrenceRule and IsBlock in my existing code I set all doctor's values as dash -, below coding is what I am trying, I try to hardcode the value - to test, but it doesn't work.
const getOrganizer = useCallback(() => {
getSchedulerOrganizerList().then((response) => {
var data = response.data;
setOrganizerList(
data.map((schedulerBlock) => {
var startDate = new Date(
formatDateStringAsDate2(schedulerBlock.startDate)
);
var endDate = new Date(
formatDateStringAsDate2(schedulerBlock.stopDate)
);
var count = null;
var repeatType = schedulerBlock.repeatType.toUpperCase();
if (repeatType === "DAILY") {
var Difference_In_Days = getDayDiff(startDate, endDate) + 1;
count = ";COUNT=" + Difference_In_Days;
} else if (repeatType === "WEEKLY") {
var Difference_In_Weeks = getWeekDiff(startDate, endDate) + 1;
count = ";COUNT=" + Difference_In_Weeks;
} else if (repeatType === "MONTHLY") {
var Difference_In_Months = getMonthDiff(startDate, endDate) + 1;
count = ";COUNT=" + Difference_In_Months;
} else if (repeatType === "YEARLY") {
var Difference_In_Years = getYearDiff(startDate, endDate) + 1;
count = ";COUNT=" + Difference_In_Years;
} else if (repeatType === "ONE TIME") {
repeatType = "ONE TIME";
}
var formatStopDate = moment(schedulerBlock.stopDate).format("YYYY-MM-DD");
var formatStopTime = moment(schedulerBlock.stopTime).format("HH:mm:ss");
var stopTimeValue = formatStopDate + "T" + formatStopTime;
var recurrenceRule = repeatType === "ONE TIME" ? null : "FREQ=" + repeatType + count;
var endTimeValue = repeatType === "ONE TIME" ? formatDateTimeStringAsDateTime(stopTimeValue) : formatDateTimeStringAsDateTime(schedulerBlock.stopTime);
var schedulerBlockDoctor = schedulerBlock.doctor;
if (schedulerBlockDoctor === "-"){
var schedulerBlockDoctor = " ";
}
return {
Id: schedulerBlock.id,
Subject: schedulerBlock.reason,
Doctor: schedulerBlockDoctor,
StartTime: formatDateTimeStringAsDateTime(schedulerBlock.startDate),
EndTime: endTimeValue,
RecurrenceRule: recurrenceRule,
IsBlock: true,
};
})
);
});
}, []);
I want the expected result like below the picture if I choose all doctors to need to block at the same time and date (all doctors value is put dash - to declare),
Hope anyone can guide me on how to solve this problem, is possible to get a simple way to edit my existing code? Thanks.

Google Apps Script: Creating and populating a nested JSON object

I would like to create a JSON object with the following structure:
var exec_log = {
total_correct: 0,
total_error: 0,
[exec_detail: {
timestamp: "1a",
script: "New Deal",
type: "Bot",
id: "2a",
pair: "3a",
status: "4a",
message: "5a"
},
exec_detail: {
timestamp: "1a",
script: "New Deal",
type: "Bot",
id: "2a",
pair: "3a",
status: "4a",
message: "5a"
},
...]
}
However I don't know how to create an array of objects inside an object or how to access them to populate them correctly.
Current code:
So far I have achieved this but I'm sure the array is not defined correctly and I suspect that I have to create an external array and push it inside the exec_log object.. Should I define 2 separated objects and push one inside the other?
function StartNewDeal(filterAPC){
var exec_log = {
total_correct: 0,
total_error: 0,
exec_detail: {
timestamp: "",
script: "New Deal",
type: "Bot",
id: "",
pair: "",
status: "",
message: ""
}
}
for(var i = 0; i < filterAPC.length; i++){
Logger.log("Passing botID: " + filterAPC[i][1])
var new_deal = StartDeal(filterAPC[i][1]);
var currentDate = Utilities.formatDate(new Date(), "PST", "yyyy-MM-dd HH:mm");
exec_log.exec_detail[timestamp[i]] = currentDate;
exec.log.exec_detail[id[i]] = filterAPC[i][1];
exec_log.exec_detail[pair[i]] = new_deal.pair;
if(new_deal.valid == false){
exec_log.exec_detail[status[i]] = "Error";
exec_log.exec_detail[message[i]] = new_deal.json;
exec.log.total_error = exec.log.total_error + 1;
}else{
exec_log.exec_detail[status[i]] = "Completed";
exec_log.exec_detail[message[i]] = "Completed";
exec.log.total_correct = exec.log.total_correct + 1;
}
}
return exec_log;
}
function createObject() {
let obj={property1:'value1',property2:'value2',property3:[],property4:[],property5:'value5',property6:'value6'};
for(let i=0;i<2;i++) {
let iObj={};
for(let j=0;j<5;j++) {
iObj['item'+j]='donkey'+j;
}
obj.property3.push(iObj);
obj.property4.push(iObj);
}
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput('<textarea>' + JSON.stringify(obj) + '</textarea>'), "Making an object with no parts");
}
Try this:
function StartNewDeal(filterAPC){
var exec_log = {
total_correct: 0,
total_error: 0,
exec_details_list: [] // an array to store your exect_details objects
}
for(var i = 0; i < filterAPC.length; i++) {
Logger.log("Passing botID: " + filterAPC[i][1])
var new_deal = StartDeal(filterAPC[i][1]);
var timestamp = Utilities.formatDate(new Date(), "PST", "yyyy-MM-dd HH:mm");
var id = filterAPC[i][1];
var pair = new_deal.pair;
var status;
var message;
var script; // doesn't seem like you're using this
if (new_deal.valid == false){
status = "Error";
message = new_deal.json;
exec.log.total_error = exec.log.total_error + 1;
} else{
status = "Completed";
message = "Completed";
exec.log.total_correct = exec.log.total_correct + 1;
}
exec_log.exec_details_list.push(new ExecDetailObject(timestamp, script, type, id, pair, status, message));
}
return exec_log;
}
// A function to create new exec_detail objects
// from your code you don't seem to be using the script property. Not sure if that's intentional
function ExecDetailObject(timestamp, script, type, id, pair, status, message) {
this.timestampt = timestamp;
this.script = script;
this.type = type;
this.id = id;
this.pair = pair;
this.status = status;
this.message = message;
}

SwiftUI Schedule Countdown Timer - Pause & Start

I'm trying to make Clock(actual time) / Timer (CountDown) to my app.. I know my solution is kind of rough, but everything works kind of ok BUT. When I pause my Countdown and then start it again - In the first time Counter adds time (for like a second) that passed since i press Pause button and afterwards loads my timeCounter value that i saved and continues counting down.. Why? Also if you have better smoother solution I will be very pleased.. THANKS!
import SwiftUI
struct Clock : View {
#State private var nowDate: Date = Date()
#State private var referenceDate: Date = Date()
#State private var display: Bool = true
#State private var updateTimer: Timer?
#State private var timeCounter: Double = 0
#State private var timerRunning: Bool = false
var body: some View {
VStack {
Button(action: {
self.display.toggle()
}) {
Text(display ? "COUNTDOWN" : "TIME")
}
Text(display ? "\(timeString(date: nowDate))" : "\(countDownString(for: referenceDate))")
HStack {
Button(action: {
self.display = false
self.timeCounter = 10
self.referenceDate = Date(timeIntervalSinceNow: self.timeCounter)
}) {
Text("10")
}
}
Button(action: {
if self.timerRunning {
self.referenceDate = Date(timeIntervalSinceNow: self.timeCounter)
self.startTimer()
self.timerRunning.toggle()
} else {
self.updateTimer?.invalidate()
self.timerRunning.toggle()
}
}) {
Text(timerRunning ? "START" : "PAUSE")
}
}
.onAppear {
self.startTimer()
}
}
//MARK: Timer
func startTimer() {
self.updateTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.nowDate = Date()
if self.referenceDate.distance(to: self.nowDate) >= 0 {
self.referenceDate = Date(timeIntervalSinceNow: 0)
self.display = true
}
self.timeCounter -= 1
}
}
//MARK: - CountDown Timer
func countDownString(for date: Date) -> String {
let calendar = Calendar(identifier: .gregorian)
let components = calendar
.dateComponents([.hour, .minute, .second],
from: nowDate,
to: referenceDate)
return String(format: "%02d:%02d:%02d",
components.hour ?? 00,
components.minute ?? 00,
components.second ?? 00)
}
//MARK: - Clock Timer
var timeFormat: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
return formatter
}
func timeString(date: Date) -> String {
let time = timeFormat.string(from: date)
return time
}
}
Ive seen lots of complicated ways to do this on the net.
New in iOS 14:
let closeDate = Date(timeIntervalSinceNow: 60.0)
Text(closeDate, style: .relative)
maybe you need Timer publisher
https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-a-timer-with-swiftui

'inout [int]' (aka 'inout Array<int>') is not convertible to 'Array<Element>'

The code is to store activity data into a bar chart, however, the last line is flagging up this error of which I have never come across.
Code:
var days:[String] = []
var stepsTaken:[Int] = []
let activityManager = CMMotionActivityManager()
let pedoMeter = CMPedometer()
let formatter = NSDateFormatter()
formatter.dateFormat = "d MMM"
dispatch_sync(serialQueue, { () -> Void in
let today = NSDate()
for day in 0...6{
let fromDate = NSDate(timeIntervalSinceNow: Double(-7+day) * 86400)
let toDate = NSDate(timeIntervalSinceNow: Double(-7+day+1) * 86400)
let dtStr = formatter.stringFromDate(toDate)
self.pedoMeter.queryPedometerDataFromDate(fromDate, toDate: toDate) { data, error in
if let data = data {
if(error == nil){
print("\(dtStr) : \(data.numberOfSteps)")
self.days.append(dtStr)
self.stepsTaken.append(Int(data.numberOfSteps))
print("Days :\(self.days)")
print("Steps :\(self.stepsTaken)")
if(self.days.count == 7){
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
let xVals = self.days
var yVals: [BarChartDataEntry] = []
for idx in 0...6 {
yVals.append(BarChartDataEntry(value: Float(self.stepsTaken[idx]), xIndex: idx))
}

Swift array. trying to find the next time in array based on current time

I have the current time and this array of times. I would like to work out which is the closest next time to the current time.
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Hour, .Minute], fromDate: date)
let hour = components.hour
let minutes = components.minute
let currentTime = "\(hour)" + ":" + "\(minutes)" //output 15:24
let timesArray = ["5:45", "6:35", "7:00", "7:30", "7:50", "8:20", "8:40", "9:15", "10:10", "11:10", "12:40", "14:15", "14:50", "15:40", "16:10", "17:10", "17:40", "18:40", "19:25", "20:50"]
let date = NSDate()
let timesArray = ["5:45", "6:35", "7:00", "7:30", "7:50", "8:20", "8:40", "9:15", "10:10", "11:10", "12:40", "14:15", "14:50", "15:40", "16:10", "17:10", "17:40", "18:40", "19:25", "20:50"]
// create a method to convert your time to minutes
func stringToMinutes(input:String) -> Int {
let components = input.componentsSeparatedByString(":")
let hour = Int((components.first ?? "0")) ?? 0
let minute = Int((components.last ?? "0")) ?? 0
return hour*60 + minute
}
//create an array with the minutes from the original array
let timesMinutesArray:[Int] = timesArray.map { stringToMinutes($0) }
let dayMinute = stringToMinutes("15:24")
// filter out the times that has already passed
let filteredTimesArray = timesMinutesArray.filter{$0 > dayMinute }
// get the first time in your array
if let firstTime = filteredTimesArray.first {
// find its position and extract it from the original array
let item = timesArray[timesMinutesArray.indexOf(firstTime)!] // "15:40"
}
If you need you can also create an extension to use it anywhere in your code as follow:
extension String {
var hourMinuteValue: (hour:Int,minute:Int) {
guard
let hour = Int(componentsSeparatedByString(":").first ?? ""),
let minute = Int(componentsSeparatedByString(":").last ?? "")
where componentsSeparatedByString(":").count == 2
else { return (0,0) }
return (hour,minute)
}
var dayMinute:Int {
return hourMinuteValue.hour*60 + hourMinuteValue.minute
}
}
let time = "15:24"
let times = ["5:45", "6:35", "7:00", "7:30", "7:50", "8:20", "8:40", "9:15", "10:10", "11:10", "12:40", "14:15", "14:50", "15:40", "16:10", "17:10", "17:40", "18:40", "19:25", "20:50"]
let minutes = times.map{ $0.dayMinute }
let filteredMinutes = minutes.filter{ $0 > time.dayMinute }
if let firstTime = filteredMinutes.first {
let item = times[minutes.indexOf(firstTime)!] // "15:40"
}

Resources