I would like to find out the index of an array by an NSDate.
Ill tried:
var sectionsInTable = [NSDate]()
let indexOfElement = sectionsInTable.indexOf(date) // where date is an NSDate in my sectionsInTable Array
print(indexOfElement)
But ill always get false
How is it possible to get the index of an NSDate from an array?
Thanks in advance.
If you have exact copies of NSDate objects, your code should work:
let date = NSDate()
let date2 = date.copy() as! NSDate
var sectionsInTable: [NSDate] = [date]
let indexOfElement = sectionsInTable.indexOf(date2)
print(indexOfElement)
//prints: Optional(0)
Your approach should work fine. This code produces an index of 2:
let s = 31536000.0 // Seconds per Year
var tbl = [NSDate]()
tbl.append(NSDate(timeIntervalSince1970: 40*s)) // 0
tbl.append(NSDate(timeIntervalSince1970: 41*s)) // 1
tbl.append(NSDate(timeIntervalSince1970: 42*s)) // 2
tbl.append(NSDate(timeIntervalSince1970: 43*s)) // 3
let date = NSDate(timeIntervalSince1970: 42*s)
let indexOfElement = tbl.indexOf(date)
The most likely reason that you are not getting the proper index is that your search NSDate has a time component that does not match the time component in NSDate objects in the array. You can confirm that this is the case by printing both objects, and verifying their time component.
Since comparison depends on how deep you want to go with date time thing. I think you should just loop through your date array and compare if it's equal and return that index.
Related
Im trying to accomplish this: [Swift2 - Sort multiple arrays based on the sorted order of another INT array
But I have to sort by NSDate array.
// dates are actual NSDates, I pasted strings into my sample
var dates:[NSDate] = [2019-12-20 13:00 +0000, 2019-12-20 13:00 +0000, 2019-12-12 13:00 +0000]
var people:[String] = ["Harry", "Jerry", "Hannah"]
var peopleIds:[Int] = [1, 2, 3, 4]
// the following line doesn't work.
// i tried a few variations with enumerated instead of enumerate and sorted instead of sort
// but with now luck
let sortedOrder = dates.enumerate().sort({$0.1>$1.1}).map({$0.0})
dates = sortedOrder.map({points[$0]})
people = sortedOrder.map({people[$0]})
peopleIds = sortedOrder.map({peopleIds[$0]})
Change your dates declaration from NSDate to Date. Date conforms to Comparable protocol since Swift 3.0. Doing so you can simply sort your dates dates.sorted(by: >). To sort your arrays together you can zip your arrays, sort it by dates and map the sorted elements:
let sortedZip = zip(dates, zip(people, peopleIds)).sorted { $0.0 > $1.0}
let sortedPeople = sortedZip.map{ $0.1.0 }
let sortedIDs = sortedZip.map{ $0.1.1 }
If you are trying to sort associated fields you should really consider using an object oriented approach using some kind of model, however, the following should achieve what you are trying to do:
var n = min(dates.count, people.count, peopleIds.count)
var tuples = (0 ..< n).map { (dates[$0], people[$0], peopleIds[$0]) }
.sorted { $0.0 as Date > $1.0 as Date }
dates = tuples.map{ $0.0 }
people = tuples.map{ $0.1 }
peopleIds = tuples.map{ $0.2 }
I’m using Xcode 7.3 and Swift 2.2 with an NSFetchedResultsController. Is it possible to create the required fetch request configured with a predicate and sort descriptors to solve this problem?
Given a Person entity that has a birthDate attribute how do I configure a fetch request to return upcoming birthdays? Here’s what I have tried:
I created a transient attribute called birthDateThisYear and configured it to return the person’s birthday this year. But I discovered that you can’t use a transient attribute in a fetch request with Core Data.
I tried the accepted answer here by denormalizing with birthDateDayNumber and birthDateMonthNumber attributes along with a custom setter for birthDate but I couldn’t figure out the predicate. What if today was Dec 31? Somehow it would need to wrap around to include Jan, Feb, and Mar.
I read that it could be done with expressions and comparison predicates. But I couldn’t figure out a solution. Anyone got this working?
I thought it work to create a denormalized attribute called birthDateInYear2000 but, again, that suffers from the same overlap problem.
Any ideas?
Suggestion:
Fetch all birthdays.
Map the birthdays to the next occurrence from now
let calendar = NSCalendar.currentCalendar()
let nextBirthDays = birthdays.map { (date) -> NSDate in
let components = calendar.components([.Day, .Month], fromDate: date)
return calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchNextTime)!
}
Sort the dates
let sortedNextBirthDays = nextBirthDays.sort { $0.compare($1) == .OrderedAscending }
Now sortedNextBirthDays contains all upcoming birthdays sorted ascending.
In Core Data you could fetch records as dictionary with birthday and objectID (or full name), create a temporary struct, map and sort the items and get the person for the objectID (or use the full name) – or you even could apply the logic to an NSManagedObject array
Edit
Using an NSFetchedResultsController you can sort the table view only if the information about the next birthday is stored in the persistent store (assuming it's the MySQL-store), because you can't apply sort descriptors including keys pointing to transient or computed properties.
The best place to update the nextBirthDate property is just before creating the NSFetchedResultsController instance of the view controller.
Create an (optional) attribute nextBirthDate in the entity Person
Create a extension of NSDate to calculate the next occurrence of a date from now
extension NSDate {
var nextOccurrence : NSDate {
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Day, .Month], fromDate: self)
return calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchNextTime)!
}
}
In the closure to initialize the NSFetchResultsController instance add code to update the nextBirthDate property of each record
lazy var fetchedResultsController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "Person")
do {
let people = try self.managedObjectContext.executeFetchRequest(fetchRequest) as! [Person]
people.forEach({ (person) in
let nextBirthDate = person.birthDate.nextOccurrence
if person.nextBirthDate == nil || person.nextBirthDate! != nextBirthDate {
person.nextBirthDate = nextBirthDate
}
})
if self.managedObjectContext.hasChanges {
try self.managedObjectContext.save()
}
} catch {
print(error)
}
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptors = [NSSortDescriptor(key:"nextBirthDate", ascending: true)]
fetchRequest.sortDescriptors = sortDescriptors
...
If the view controller can edit the birthDate property don't forget to update the nextBirthDate property as well to keep the view in sync.
my suggestion is similar:
fetch all customers/persons
filter all birthdates that range 3 days before and 3 days after today
Use closure like the following:
func fetchBirthdayList(){
// this example considers all birthdays that are like
// |---TODAY---|
// Each hyphen represents a day
let date = Date()
let timeSpan = 3
let cal = Calendar.current
nextBirthDays = self.list.filter { (customer) -> Bool in
if let birthDate = customer.birthDate {
let difference = cal.dateComponents([.day,.year], from: birthDate as! Date, to: date)
return (( (difference.day! <= timeSpan) || ((365 - difference.day!) <= timeSpan) ) && difference.day! >= 0)
}
return false
}
}
If you want, you can order the result afterwards.
Cheers
Oliver
I have an Array: var messageArray = [AnyObject]() and in that Array there is a single tuple that contains Dictionaries with 10 key/value paires (9 of them not important for the sort): var messageDetailDict = [String: AnyObject]()
Getting and setting those values all work correctly, however now I want to sort the Array by 1 of the values (not keys) of the Dictionary.
Example -> The Array has a tuple containing several Dictionaries:
The key in the Dictionary (which is the first element in the Array) is: 'ReceivedAt' which has a value of 21-03-2015
The key in the Dictionary (which is the second element in the Array) is: 'ReceivedAt' which has a value of 20-03-2015
The key in the Dictionary (which is the third element in the Array) is: 'ReceivedAt' which has a value of 15-03-2015
Now the Array should be sorted so that the values of 'ReceivedAt' will be sorted from earliest date, to the last date.
Hope this makes sense, but it's a bit difficult to explain. Thanks!
EDIT >>>>>
This is the println(messageArray) output:
[(
{
ConversationId = "94cc96b5-d063-41a0-ae03-6d1a868836fb";
Data = "Hello World";
Id = "eeb5ac08-209f-4ef0-894a-72e77f01b80b";
NeedsPush = 0;
ReceivedAt = "/Date(1439920899537)/";
SendAt = "/Date(1436620515000)/";
Status = 0;
},
{
ConversationId = "94cc96b5-d063-41a0-ae03-6d1a868836fb";
Data = "Hello World";
Id = "86b8766d-e4b2-4ef6-9112-ba9193048d9d";
NeedsPush = 0;
ReceivedAt = "/Date(1439921562909)/";
SendAt = "/Date(1436620515000)/";
Status = 0;
}
)]
And the received date is converted to a string with the following method (I do think however this is not important, as it is a time interval, and therefore OK to sort):
func getTimeStampFromAPIValue(dateTimeReceived: String) -> String {
let newStartIndex = advance(dateTimeReceived.startIndex, 6)
let newEndIndex = advance(dateTimeReceived.endIndex, -2)
let substring = dateTimeReceived.substringWithRange(newStartIndex..<newEndIndex) // ell
let receivedAtValueInInteger = (substring as NSString).doubleValue
let receivedAtValueInDate = NSDate(timeIntervalSince1970:receivedAtValueInInteger/1000)
//format date
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yy hh:mm"
var dateString = dateFormatter.stringFromDate(receivedAtValueInDate)
return dateString
}
Since the values of ReceivedAt are timestamps as strings you could apply the following algorithm:
var sortedArray = messageArray.sorted { (dict1, dict2) in
// Get the ReceivedAt value as strings
if let date1String = dict1["ReceivedAt"] as? String,
let date2String = dict2["ReceivedAt"] as? String {
// Compare the date strings to find the earlier of the two
return date1String.compare(date2String) == .OrderedAscending
}
// Couldn't parse the date, make an assumption about the order
return true
}
Try this, change OrderedAscending with OrderedDescending if need in inverse order
messageArray.sortInPlace {
($0["ReceivedAt"] as! NSDate).compare($1["ReceivedAt"] as! NSDate) == .OrderedAscending
}
I have an Int, say 15344 and I'm sending it to a label as a string, but I want it to be formatted as 01:53.44
var split = data.valueForKeyPath("time") as! Int
cell.textLabel?.text = split.description
but this only gives me the 15344 without format. I tried .stringbyAppendingformat but couldn't get it right. thanks in advance!
That is a strange way to store a time and perhaps you should think
about using the number of milliseconds instead. If that is not an option,
you can "dissect" the integer with
let time = 15344
let minutes = time / 10000
let seconds = (time / 100) % 100
let centis = time % 100
and then create a string with
let text = String(format:"%d:%02d.%02d", minutes, seconds, centis)
print(text) // 1:53.44
The simple solution would be
First convert it to string then,
let time = "15344"
let rangeOfSecond = Range(start: (advance(time.endIndex, -2)),
end: time.endIndex)
let secondString = time.substringWithRange(rangeOfSecond)
For minute
let rangeOfMinute = Range(start: (advance(time.endIndex, -4)),
end: time.endIndex - 2)
let minuteString = time.substringWithRange(rangeOfMinute)
Similarly for hour. Then concat all substrings with ":".
This seems like a weird way to be doing time formatting. There is an NSDate api that uses NSDateFormatter for this purpose. The documentation can be found here:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/
You might want to use:
func stringFromDate(_ date: NSDate) -> String
I'm a beginner in Swift and coding in general. Right now I'm trying to develop the piece of the code to set up the time limit for the action only once a day.
#IBAction func yesButtonPressed(sender: AnyObject) {
//To retrive the control date value. First time it has nil value
var controlDate = NSUserDefaults.standardUserDefaults().objectForKey("controlDate") as? NSDate
//To check current date to compare with
var currentDate = NSDate()
// To check if time interval between controlDate and currentDate is less than 1 day
var timeInterval = controlDate?.timeIntervalSinceNow
var dayInSeconds = 24 * 3600
if timeInterval < dayInSeconds {
//show alert with message "You've done it recently. Pls wait a bit"
} else {
//perfome the action
//update the value of NSUserDefaults.standardUserDefaults().objectForKey("controlDate") with current time stamp
}
}
Instead of checking if the controlTime var has nil value to catch the App first time running I was trying to develop some shorter, universal code for both case, first time and the rest times when the controlDate var will be saved in UserDefaults.
Nevertheless it doesn't work properly (( I'd appreciate your help a lot!
dayInSeconds is the wrong type to make the comparison. Type inference is determining that it should be an Int while timeIntervalSinceNow is a Double under the covers. And thus, you can't definitively compare an Int and a Double with accuracy.
Create dayInSeconds in this way so that it's type is inferred as a Double, rather than an Int.
var controlDate = NSUserDefaults.standardUserDefaults().objectForKey("controlDate") as? NSDate
var currentDate = NSDate()
var timeInterval = controlDate?.timeIntervalSinceNow
//*** HERES THE CHANGE***
var dayInSeconds = 24.0 * 3600
if timeInterval < dayInSeconds {
}
Since you aren't attempting to call a method on timeInterval optional unwrapping is not necessary in this case.
timeInterval is an Optional, you need to unwrap it first
if let ti = timeInterval {
if ti < dayInSeconds {
...
}
}
More on Optionals