I have an array called thoughtArray which is an array of a custom object called ThoughtObject. ThoughtObject has a property called 'createdDate' which holds an NSDate, the date that the object was created.
I need to filter through that array and find all the objects that match the current date and then append them to another array.
So far all attempts have been unsuccessful. This is what i've tried below.
for createdToday in thoughtArray {
if (createdToday.createdDate?.isEqualToDate(NSDate()) != nil) {
createdTodayArray.append(createdToday)
}
}
The problem is that even objects that have the createdToday property set to a few days ago get added to the array.
Help would be greatly appreciated.
There are two problems. First, the "optional chaining"
createdToday.createdDate?.isEqualToDate(NSDate()
returns nil or not, depending on whether createdToday.createdDate
is nil or not. That is not what you want.
Second, as already stated by #thelaws in his answer, isEqualToDate()
returns only yes if the two dates represent exactly the same moment in time. NSCalendar has a
func compareDate(date1: NSDate, toDate date2: NSDate, toUnitGranularity unit: NSCalendarUnit) -> NSComparisonResult
method (available since iOS 8) which can be used here:
let cal = NSCalendar.currentCalendar()
let now = NSDate()
for createdToday in thoughtArray {
if let createdAt = createdToday.createdDate {
// compare with "day granularity":
if cal.compareDate(createdAt, toDate: now, toUnitGranularity: .CalendarUnitDay) == .OrderedSame {
createdTodayArray.append(createdToday)
}
}
}
An NSDate object represents a specific moment in time. Thus, it's very unlikely that a date in your array represents exactly NOW.
There are several approaches to determine if a date is today. You could turn the date into NSDateComponents and compare the year, month and day.
Use the compareDate function mentioned by Martin and the filter function of Array:
var thoughtArray = [ThoughtObject]()
var cal = NSCalendar.currentCalendar()
var createdToday = thoughtArray.filter {
if let createdDate = $0.createdDate {
return cal.compareDate(NSDate(), toDate: createdDate, toUnitGranularity: .CalendarUnitDay) == .OrderedSame
}
else {
return false;
}
}
Related
This question already has answers here:
how to find/filter the array element with the smallest positive difference
(7 answers)
Closed 12 months ago.
I have seen questions like these, but from like 6 years ago and do not directly answer the question in my opinion.
I have an array of 'events' which follow the Model of:
struct Event: Identifiable{
var id = UUID().uuidString
var eventTitle: String
var eventDescription: String
var eventDate: Date
}
And example data of:
#Published var storedEvents: [Event] = [
Event(eventTitle: "Go shopping", eventDescription: "Eggs, flour, butter", eventDate: .init(timeIntervalSince1970: 1646078400)),
Event(eventTitle: "Lecture with bob", eventDescription: "Room 453", eventDate: .init(timeIntervalSince1970: 1646078708))
]
For any given event, I'd like the event's date to be passed to a function that takes Date and returns true or false whether the given events date is the closest/current event at the current date time compared to all other event's dates (i.e. if I had two events, one for 8:15 and one for 8:30 and the current time is 8:21, then the first event should be the current one, and therefore returned true from my function.
I have a function working that only takes into account the hour, so if two events have the same hour then they will both be selected and both return true:
func isCurrentHour(date: Date)-> Bool{
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let currentHour = calendar.component(.hour, from: Date())
return hour == currentHour
}
I have tried sorting the list of dates from the earliest to latest and then comparing each to the current time but I'm not sure how to return true or false on the right event being compared as I have no way of identifying which date belongs to an event.
I think you may be going about this the hard way. It seems what you really want is the earliest event from all events. If so, that can be accomplished like this:
First, make your Event struct conform to Comparable by date:
struct Event: Identifiable, Comparable {
var id = UUID().uuidString
var eventTitle: String
var eventDescription: String
var eventDate: Date
static func < (lhs: Event, rhs: Event) -> Bool {
lhs.eventDate < rhs.eventDate
}
}
Then you can return the minimum element in the array. The following will return the earliest event in time, but remember that there is no check in it to make sure that it is the earliest future event, if that is what you need.
private func earliestEvent(events: [Event]) -> Event? {
return events.min()
}
This returns an optional Event as it could fail, such as in the case where the array is empty.
Edit:
private func earliestEvent(event: Event, events: [Event]) -> Bool {
guard let earliestEvent = events.min() else { return false }
return event == earliestEvent
}
I suggest as a solution to just take the intervals from the dates so far and then compare the two intervals.
func timeIntervalSince(_ date: Date) -> TimeInterval
Documentation here
I am working on code where I am receiving lots of data associated with dates
each object having one date parameter and there might many objects with the same date.
I need to show this all objects in UITableView. each object as one cell.
I succeed in that,
I need to get unique dates from the response array of objects.
Those unique dates will be stored in an array which will act as a number of sections of my table view with section header title will be the date from the unique date array.
somehow I am able to sort out that with what I want,
The only problem I am facing is I am not able to sort the unique date array
every time the sequence change.
I need the latest date as the first date and the oldest date as the end date.
How to achieve this in swift.
Following is a piece of code I have written
let sortedKeys = Array(dictValue.keys).sorted(by: {$0 > $1})
print(sortedKeys)
here dicValue.keys is my unique date array and I wanted to sort it.
Following is a sample response I am getting
["08/03/2021”, “10/02/2021”, "26/04/2021", "25/03/2021”, "09/12/2020”, , "27/04/2021”, "23/03/2021”, "11/01/2021”, "05/03/2021”, "09/03/2021”, "16/10/2020", "19/03/2021", "12/10/2020" ]
and after applying sort I am getting the following output
[“27/04/2021", "26/04/2021", "25/03/2021", "23/03/2021", "19/03/2021", "16/10/2020", "12/10/2020", "11/01/2021", "10/02/2021", "09/12/2020", "09/03/2021", "08/03/2021", "05/03/2021”]
where dates are not properly sorted out.
Can anyone please help me out with it.
Thanks in advance.
This string date format is inappropriate for sorting, because the most significant component is day. Only a date format like yyyy/MM/dd can be sorted properly by comparison operator >.
However this is Swift. The closure can contain anything as long as it returns a Bool. You could sort the array with a custom sort algorithm. It splits the strings into components and sorts first year then month then day
let sortedKeys = dictValue.keys.sorted { (date1, date2) -> Bool in
let comps1 = date1.components(separatedBy: "/")
let comps2 = date2.components(separatedBy: "/")
return (comps1[2], comps1[1], comps1[0]) > (comps2[2], comps2[1], comps2[0])
}
print(sortedKeys)
If you want to sort a date, just sort a Date. Date supports Hashable and can be used as a dictionary key, you could map your original dictionary and by using a DateFormatter to format your string keys into Dates then you can easily sort them.
let dictionary = ["08/03/2021": 2, "10/02/2021": 5, "26/04/2021" : 6]
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy" // You should probably adjust other properties of the formatter
let newDict = Dictionary(uniqueKeysWithValues:
dictionary.map { (key, value) -> (Date, Int) in
print("Key: \(key)")
return (formatter.date(from: key)!, value)
})
let sortedDates = newDict.keys.sorted { $0 > $1 }
let value = newDict[sortedDates[0]]
My app has a custom object SSSchedule that I persist in CoreData, with a sortDescriptor of "date" (SSSchedule has a variable var date : NSDate?). Is there a more efficient method to finding a specific SSSchedule object with a specific date rather than iterating through the fetchedObjects array checking each for schedule.date == myDate as! NSDate?
My app references the fetchedObjects quite often, so I would imagine constantly mapping fetchedObjects to a dictionary of type [String : SSSchedule] (for example) every time the context is saved would affect performance...
Write a fetch request to return the objects matching that specific date from the datastore. If you are being consistent, then from what you've written you'll get back an array with one element.
Let Core Data do that searching for you. That's what it's for.
I think if you use a plist which has a Dictionary of Dictionaries. it could be a more what you need.
The first Dictionary will have a key of a tuple of (Day,Month,Year) which can be easily extracted from NSDate. and a value of a Dictionary which key is a tuple of (Hours, Minutes) also extracted from NSDate and a value of String which is the task to do at that time.
this way if you have a specific date, that date is the key to access only the tasks and events you have during that specific date in O(1) time complexity.
Now if you want to know if you have something at a specific time you access it in a similar way. The method is supposed to return String?. If there's a task at a specific time, it will return the task, otherwise it will return nil which means you're free at this time.
This is how the data structure should look [(Day, Month,Year):[(Hours,Minutes):String]]
Regarding extracting components from NSDate
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour |.CalendarUnitMinute
|.CalendarUnitYear|.CalendarUnitMonth|.CalendarUnitDay, fromDate: date)
let day = components.day
let month = components.month
let year = compononets.year
let hour = components.hour
let minutes = components.minutes
Use filter() to build a new array with any objects that match your criteria:
let newArray = fetchedObjects.filter() { $0.date == myDate as! NSDate }
Then check the count of newArray and handle accordingly - unless you know the dates are unique, there could be zero, one or more elements in the array.
I have the a Person object store in Core Data which contains two attributes:
name
payment
Now I decided I want to also store the average of payments for a day/month/year.
How should I store this inside Core Data ? Inside the Person object ? Create new objects ?
The solution is not to store summary data. You can add convenience methods to your managed object subclass to deliver the desired value. Something along these lines:
func averageForPeriod(startDate: NSDate, endDate: NSDate) -> Double {
guard self.payments != nil else { return 0 }
let periodPayments = (self.payments as Set<Payment>).filter {
$0.date >= startDate && $0.date <= endDate
}
return periodPayments.valueForKeyPath("#avg.amount").doubleValue
}
NB: for comparing dates like this you need to define your own comparison function as shown here, or you can use NSTimeInterval.
I have an array of objects of type Thing:
class Thing: NSObject {
var data: String
var type: String
var created: NSDate
}
These things have an NSDate property called created. My aim is to write a function that reads the created property of every thing in the array and returns the thing that has the most recent date. The function looks like this:
public func getLastSwipe(list: Array<Thing>) -> Thing {
return someThing
}
Another approach is using Swift's .max, like this:
dates.max(by: <)
The following is my old answer. The above is updated in feb 2023.
let mostRecentDate = dates.max(by: {
$0.timeIntervalSinceReferenceDate < $1.timeIntervalSinceReferenceDate
})
This is the most performant solution I've found.
Returns the sequence’s most recent date if the sequence is not empty; otherwise, nil.
You could use reduce if you wanted. This will find the object with the highest timestamp.
var mostRecent = list.reduce(list[0], { $0.created.timeIntervalSince1970 > $1.created.timeIntervalSince1970 ? $0 : $1 } )
If your dates are not all in the past, you'll have to also compare against the current date to determine a cutoff. If your dates are all in the future, you'll want to switch the > to < to find the next future date (lowest timestamp).
You can sort the array, then find the first/last element. For example...
let objects: [Thing] = ... //Set the array
let mostResent = array.sorted { (firstThing, secondThing) -> Bool in
firstThing.created.timeIntervalSince1970 > secondThing.created.timeIntervalSince1970
}.first
This will return the most resent Thing as an Optional (because there is no guarantee that the array is not empty. If you know that the array is not empty, then you can end that line with .first!