How to get the latest date from array in Swift - ios

I have an array of dates. I need to find the latest one. Can someone show me an example?

You can make NSDate conform to Comparable, as shown here
Given this, you can then use maxElement to find the maximum (i.e. the latest).
import Foundation
extension NSDate: Comparable { }
public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.isEqualToDate(rhs)
}
public func <(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == .OrderedAscending
}
let dates = [NSDate(), NSDate()]
let maxDate = maxElement(dates)
Note, maxElements goes bang for empty arrays so you may want to guard it with isEmpty:
let maxDate = dates.isEmpty ? nil : Optional(maxElement(dates))
Or, if you don’t want to go the extension route:
if let fst = dates.first {
let maxDate = dropFirst(dates).reduce(fst) {
$0.laterDate($1)
}
}
or, to return an optional:
let maxDate = dates.reduce(nil) {
(lhs: NSDate?, rhs: NSDate?)->NSDate? in
lhs.flatMap({rhs?.laterDate($0)}) ?? rhs
}

You can make use of reduce:
guard let dates = dates, !dates.isEmpty else { return nil }
dates.reduce(Date.distantPast) { $0 > $1 ? $0 : $1 }
Edit: Handle empty or nil array

Swift has Array methods for getting both the min and max values for dates.
You can use the following:
let maxDate = myArrayOfDates.max()
let minDate = myArrayOfDates.min()
So if you have an array of dates like so:
And here is the code if you want to copy it:
let now = Date()
let dates = [
now,
now.addingTimeInterval(120),
now.addingTimeInterval(60)
]
let sut = dates.max()
print(sut!)
Hope this helps someone!

Run this in your playground
var dates = [NSDate]()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
let date1 = dateFormatter.dateFromString("02-06-1987")
let date2 = dateFormatter.dateFromString("02-06-2001")
let date3 = dateFormatter.dateFromString("02-06-2010")
//var date1 = NSDate()
dates.append(date3!)
dates.append(date1!)
dates.append(date2!)
var maxDate = dates[0]
for i in 0...dates.count-1
{
if(maxDate.compare(dates[i]).rawValue == -1){
maxDate = dates[i]
println(maxDate)
}
println(maxDate)
}
println(maxDate.description)
have a good day :)

Related

Sort custom objects based on comparison between values of 2 different attributes

I have a custom object class as below which consists of 3 variables:
class DateObj {
var startDate: Date?
var endDate: Date?
var updatedEndDate: Date?
}
Below are the objects I created for it:
let obj1 = DateObj.init(startDate: 1/13/2022 7:00am, endDate: 1/13/2022 6:30pm, updatedEndDate: nil)
let obj2 = DateObj.init(startDate: 1/13/2022 10:30am, endDate: 1/14/2022 10:30am, updatedEndDate: 1/13/2022 10:30pm)
let obj3 = DateObj.init(startDate: 1/13/2022 11:30am, endDate: 1/14/2022 11:30am, updatedEndDate: 1/13/2022 7:30pm)
let obj4 = DateObj.init(startDate: 1/13/2022 1:30pm, endDate: 1/13/2022 5:30pm, updatedEndDate: nil)
Doesn't matter what the start date is, I want to compare values of endTime with updatedEndTime and want to sort such that the event that ends first (end time could be in endDate or updatedEndDate) should be first in the array and the event that ends last should be last in the array.
Note: updatedEndTime will always be less than endTime since the event could have ended earlier than expected time.
var inputDates = [obj1, obj2, obj3, obj4]
var expectedOutputDates = [obj4, obj1, obj3, obj2] // Expected output after sort
Code that I tried:
inputDates.sorted { (lhs, rhs) in
if let lhsUpdated = lhs.updatedEndDate, let rhsUpdated = rhs.updatedEndDate {
return lhsUpdated < rhsUpdated
} else if let lhsUpdated = lhs.updatedEndDate, let rhsEndTime = rhs.endDate {
return lhsUpdated < rhsEndTime
} else if let lhsEndTime = lhs.endDate, let rhsUpdated = rhs.updatedEndDate {
return lhsEndTime < rhsUpdated
} else if let lhsEndTime = lhs.endDate, let rhsEndTime = rhs.endDate {
return lhsEndTime < rhsEndTime
}
return false
}
My code is not giving me the expected output. Could someone help and let me know how to compare values from 2 different attributes for sorting an array?
Thanks!

Save array of Days in Swift 5

So i have created a class for a Day and for a Drink. and I'm trying to track how much you drink in a day, but I'm struggling with saving multiple days. I'm currently managing to save the current day(with the amount drunk that day) but i don't know how to save more than one day.
I want to save an array of type Day with all the days. how can i do this?
This is my Day class:
public class Day: NSObject {
var date: Date
var goalAmount: Drink
var consumedAmount: Drink
func saveDay() {
let formatting = DateFormatter()
formatting.dateFormat = "EEEE - dd/mm/yy"
UserDefaults.standard.set(formatting.string(from: date), forKey: "date")
UserDefaults.standard.set(goalAmount.amountOfDrink, forKey: "goal")
UserDefaults.standard.set(consumedAmount.amountOfDrink, forKey: "consumed")
}
func loadDay() {
let rawDate = UserDefaults.standard.value(forKey: "date") as? String ?? ""
let formatter = DateFormatter()
formatter.dateFormat = "EEEE - dd/mm/yy"
date = formatter.date(from: rawDate)!
goalAmount.amountOfDrink = UserDefaults.standard.float(forKey: "goal")
consumedAmount.amountOfDrink = UserDefaults.standard.float(forKey: "consumed")
}
}
This is my Drink class:
class Drink: NSObject {
var typeOfDrink: String
var amountOfDrink: Float
}
i am calling saveDay() when there are any changes made to the day, and then loadDay() when the app opens.
A better approach would be is to store the object of the class in userDefaults instead of storing particular properties of that class. And use [Date] instead of Date to save multiple days
For this first, you have Serialize the object to store in userDefaults and Deserialize to fetch the data from userDefaults.
import Foundation
class Day: Codable {
var date = Date()
var goalAmount: Drink
var consumedAmount: Drink
init(date: Date, goalAmount: Drink,consumedAmount: Drink ) {
self.date = date
self.goalAmount = goalAmount
self.consumedAmount = consumedAmount
}
static func saveDay(_ day : [Day]) {
do {
let object = try JSONEncoder().encode(day)
UserDefaults.standard.set(object, forKey: "days")
} catch {
print(error)
}
}
static func loadDay() {
let decoder = JSONDecoder()
if let object = UserDefaults.standard.value(forKey: "days") as? Data {
do {
let days = try decoder.decode([Day].self, from: object)
for day in days {
print("Date - ", day.date)
print("Goal Amount - ", day.goalAmount)
print("Consumed Amount - ",day.consumedAmount)
print("----------------------------------------------")
}
} catch {
print(error)
}
} else {
print("unable to fetch the data from day key in user defaults")
}
}
}
class Drink: Codable {
var typeOfDrink: String
var amountOfDrink: Float
init(typeOfDrink: String,amountOfDrink: Float ) {
self.typeOfDrink = typeOfDrink
self.amountOfDrink = amountOfDrink
}
}
Use saveAndGet() method to store and fetch details from userDefaults
func saveAndGet() {
// use any formats to format the dates
let date = Date()
let goalAmount = Drink(typeOfDrink: "Water", amountOfDrink: 5.0)
let consumedAmount = Drink(typeOfDrink: "Water", amountOfDrink: 3.0)
let day1 = Day(date: date, goalAmount: goalAmount, consumedAmount: consumedAmount)
let day2 = Day(date: date, goalAmount: goalAmount, consumedAmount: consumedAmount)
let day3 = Day(date: date, goalAmount: goalAmount, consumedAmount: consumedAmount)
let day4 = Day(date: date, goalAmount: goalAmount, consumedAmount: consumedAmount)
let days = [day1, day2, day3, day4]
Day.saveDay(days)
Day.loadDay()
}
1) You need to create array of object for this :
goalAmount = [Drink]()
var date = [Date]()
and append with each new element.
you can also add date variable inside your drink class.
2) you can also create array of dictionary:
var userData = [String : Any]()
key will be you date and Any contain related to drink data in Any you can store Anything.

Best Practice - Sorting mixed objects in Swift

I am trying to sort an array of different NSManagedObjects in Swift. In Objective-C, it would have required just 2 lines of code (a sort descriptor and the actual sort). However, the only way I could figure out how to do it in swift required several lines of code. Is there a better and/or faster way than the code I wrote please?:
var orderMOs = [NSManagedObject]()
orderMOs.append(contentsOf: incidentMOs)
orderMOs.append(contentsOf: transferMOs)
orderMOs.sort (by: {(leftMO, rightMO) -> Bool in
var leftDate: NSDate?
var rightDate: NSDate?
if leftMO is Incident {leftDate = (leftMO as! Incident).createdDate}
else if leftMO is Transfer {leftDate = (leftMO as! Transfer).createdDate}
if rightMO is Incident {rightDate = (rightMO as! Incident).createdDate}
else if rightMO is Transfer {rightDate = (rightMO as! Transfer).createdDate}
if leftDate == nil || rightDate == nil {return true}
return leftDate!.compare(rightDate! as Date) == .orderedDescending
})
You should both your classes conform to a protocol that declares createdDate. Type orderMOs as such. than you won't need the conditional casts.
import Foundation
class A {
init(created createdDate: Date) {
self.createdDate = createdDate
}
let createdDate: Date
}
class B {
init(created createdDate: Date) {
self.createdDate = createdDate
}
var createdDate: Date
}
protocol Created {
var createdDate: Date { get }
}
extension A: Created {}
extension B: Created {}
func createDate(year: Int, month: Int, day: Int) -> Date {
var comps = DateComponents()
comps.year = year
comps.month = month
comps.day = day
return Calendar.current.date(from: comps)!
}
var objects = [Created]()
objects.append(A(created: createDate(year: 2018, month: 2, day: 1)))
objects.append(B(created: createDate(year: 2017, month: 12, day: 1)))
objects.append(B(created: createDate(year: 2018, month: 5, day: 18)))
objects.append(A(created: Date()))
Sort it like
objects.sort { (c1, c2) -> Bool in
return c1.createdDate < c2.createdDate
}
Another thing you can do is to use switch-statement with pattern matching to clean code up a bit.
Why not use KVC here?
if let leftDate = leftMO.value(forKey: "createdDate") as? Date,
let rightDate = rightMO.value(forKey: "createdDate") as? Date {
if leftDate == nil || rightDate == nil {return true}
return leftDate.compare(rightDate) == . orderedDescending
}

Swift 2 - remove NSDate values in array if value is before current date

In my Application I create an array of "NSDate" in order to send local notifications.
The values saved are "UUID" and "deadline" and they are saved using let gameDictionary = NSUserDefaults.standardUserDefaults().dictionaryForKey(GAME_INFO) ?? [:]
The result is somenting similar to this:
[{
UUID = "546C5E4D-CFEE-42F3-9010-9936753D17D85";
deadline = "2015-12-25 15:44:26 +0000";
}, {
UUID = "7C030614-C93C-4EB9-AD0A-93096848FDC7A";
deadline = "2015-12-25 15:43:15 +0000";
}]
What I am trying to achieve is to compare the "deadline" values with the current date and if the deadline is before than current date the values need to be removed from the array.
func compareDeadline() {
let gameDictionary = NSUserDefaults.standardUserDefaults().dictionaryForKey(GAME_INFO) ?? [:]
var items = Array(gameDictionary.values)
for i in 0..<items.count {
let dateNotification = items[i]["deadline"]!! as! NSDate
print(dateNotification)
var isOverdue: Bool {
return (NSDate().compare(dateNotification) == NSComparisonResult.OrderedDescending) // deadline is earlier than current date
}
print(isOverdue)
if (isOverdue == true){
items.removeAtIndex(i)
}
}
}
When I try to remove the values from the array I get Fatal Error: Array index out of range
Any Idea How can I solve this?
You should use the .filter method on the array to remove anything that you don't want in that array. The result is a new array with just the filtered results.
.filter requires you to set the filter criteria in a closure that you send into it
Here is a good article on how to use it
You can use filter method of swift array
For example to filter even numbers in array:
func isEven(number: Int) -> Bool {
return number % 2 == 0
}
evens = Array(1...10).filter(isEven)
println(evens)
There are a few problems. The reason you are getting an error is because you cannot remove elements while iterating inside for-in block. You can filter the items with the following code:
func compareDeadline() {
let gameDictionary = NSUserDefaults.standardUserDefaults().dictionaryForKey(GAME_INFO) ?? [:]
let items = Array(gameDictionary.values)
let currentDate = NSDate()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ssZ"
let filteredItems = items.flatMap({
guard let stringDeadline = $0["deadline"] as? String, let deadline = dateFormatter.dateFromString(stringDeadline) else {
return nil
}
return deadline
}).filter({
currentDate.compare($0) == .OrderedDescending
})
}

Saving `Date` to `CoreData` does not work

I am trying to save a Date into a textfield and have that save into CoreData. I have the textfield set up and am able to use the date picker just fine with the NSDateFormatter but I am having trouble with getting it to save into the textfield into CoreData.
extension NSDate{
var stringValue: String{
return self.toString()
}
func toString() -> String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let str = formatter.stringFromDate(self)
return str
}
}
extension String{
var dateValue: NSDate?{
return self.toDate()
}
func toDate() -> NSDate? {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let date = formatter.dateFromString(self) {
return date
}else{
// if format failed, Put some code here
return nil // an example
}
}
}
add this befor your class or another swift file,
then change textFieldDDate.NSDate = ddate to:
textFieldDDate.text = ddate.stringValue
you can only use text(String!) with UITextField,also only NSDate in your newItem.ddate.
change newItem.ddate = textFieldDDate.text to
newItem.ddate = textFieldDDate.text.dateValue
I see var ddate = data.valueForKey("ddate"), I guess it is type of NSDate? maybe you need change it to String, it can't be just use as!(?) String,if I am right, you need use my code of extension NSDate{} to change it too.
I checked your codes, just find some lines maybe it is save data to coreData:
if segue.identifier == "update" {
var selectedItem: NSManagedObject = myDivelog[self.tableView.indexPathForSelectedRow()!.row] as! NSManagedObject
let ADLVC: AddDiveLogViewController = segue.destinationViewController as! AddDiveLogViewController
ADLVC.divenumber = selectedItem.valueForKey("divenumber") as! String
ADLVC.ddate = selectedItem.valueForKey("ddate") as! NSDate
ADLVC.divelocation = selectedItem.valueForKey("divelocation") as! String
ADLVC.existingItem = selectedItem
}
am I right? I get this link of an answer of how to save a Data to CoreData for you. because maybe something wrong in there.
here it is https://stackoverflow.com/a/26025022/5113355

Resources