Sort received dates from response in swift - ios

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]]

Related

Most efficient way to parse through nested dictionaries [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have the following dictionary:
var masterDict = [Double : [Date:[String:Any]]]()
I would like to retrieve the following information based on this data source:
The earliest Date object to exist in the dictionary (search all Double keys).
The latest/furthest Date object to exist in the dictionary (search all Double keys).
My thoughts are to loop through the .keys of the Dictionary and then enumerate that way, but I am asking if there is a Swiftier way to accomplish retrieving this information (possibly faster too)?
First of all get the all the values from masterDict and flatten them out using flatMap(_:) create a single Array,
let values = masterDict.values.flatMap { $0 }
Next, sort the values in ascending order using sorted(_:)
let sortedValues = values.sorted { $0.key.compare($1.key) == .orderedAscending }
Now use first and last on the sortedValues to get the latest and further Date instance,
let first = sortedValues.first?.key
let last = sortedValues.last?.key
You can use reduce to extract all dates values
let allDates = masterDict.reduce(into: []) { $0.append(contentsOf: $1.value.keys) }
and then you can simply get the min and max dates
print(allDates.max())
print(allDates.min())
or if you prefer first and last you can sort the array
let allDates = masterDict.reduce(into: []) { $0.append(contentsOf: $1.value.keys) }.sorted()
print(allDates.last)
print(allDates.first)
You can use sort function on dictionary, sort condition will be based on dates comparison.
var masterDict = [Double : [Date:[String:Any]]]()
You can access latest and furthest date based on it.
let sorted = masterDict.sorted { (dict1, dict2) -> Bool in
return dict1.value.keys.first! < dict2.value.keys.first!
}
Try first and last properties on sorted array of dictionary to get the latest date.
print(sorted.first!.value)
print(sorted.last!.value)

Inserting into Array and comparing Dates Swift iOS Code

I am attempting to create an array that will store 365 integers, it must be filled completely. I am using Healthkit to figure out the users steps from a year back, hence the array size. Every integer represents 1 day.
I have done this in android already and it worked perfectly, I got 365 integers back with 0's for the days with no steps, however, the problem is with iOS health kit I get nothing from days with no data, which I need. In order to do this I thought I would compare the date variable I get with the date of the current day + 1 and loop through the array to see if it find any matching cases, if not put a 0 into it at the end.
So in order to do this I created an array of 365, at the line var ID = 0 is where I attempt to store the integers correctly into the array. I am using Swift 4.
struct stepy {
static var step = [365]
}
This is where I enumerate through the stepData, first at var ID I attempt to compare the date I get in the enumerate loop with the current date (basically index 0 in the array, which represents the first day, the current day).
However I got a problem, currently I believe I would overwrite the days which already has been inputted into the date at the second step enumeration? Also I can't get the date code to compile properly, I just get the Date has no valid member called "add"
stepsQuery.initialResultsHandler = { query, results, error in
let endDate = NSDate()
let startDate = calendar.date(byAdding: .day, value: -365, to: endDate as Date, wrappingComponents: false)
if let myResults = results{
myResults.enumerateStatistics(from: startDate!, to: endDate as Date) { statistics, stop in
if let quantity = statistics.sumQuantity(){
var date = statistics.startDate
let steps = quantity.doubleValue(for: HKUnit.count())
var id = 0
var dateToInsert = date
var today = Date()
var todaytwo = Date()
for index in 0..<stepy.step.count {
if dateToInsert != today {
id = index + 1
today.(Calendar.current.date(byAdding: .day, value: -1, to: today)
stepy.step.append(0)
}
if date == dateToInsert as Date {
today.add(Calendar.current.date(byAdding: .day, value: -1, to: today)
stepy.step.append(Int(steps))
id = index + 1
}
}
}
}
}
}
static var step = [365]
The above doesn't make sense. It does not create an array of 365 integers, it creates an array with one integer in it that is 365. What you need is
static var step: [Int] = []
which creates an empty array you can just append your results to
currently I believe I would overwrite the days which already has been inputted into the date at the second step enumeration?
because your code appends to the array, which is the same as in Java: myArrayList.add(element), this is not a problem.
Also I can't get the date code to compile properly, I just get the Date has no valid member called "add"
Correct, it doesn't. Also this line:
today.(Calendar.current.date(byAdding: .day, value: -1, to: today)
does not make any sense. That should be causing a compiler error to.
Anyway, I don't see what the point of all that is. Your outer loop presumably loops through the statistics, one per day, so just do your calculation and append to the array. It'll be oldest first, but you can then just reverse the array to get newest first.

How to search for objects using < or <= operator in realm?

Currently I have Transaction objects which have a transactionDate:
class Transaction: Object {
#objc dynamic var transactionDate: String = "" // yyyy-MM-dd
}
I have lots of these objects and I need to delete only the old ones (that are more than two year old) so I wrote:
// Since now is 2017-09 I need to delete all objects before 2015-09
// calculations are out of scope here I think :)
realm.objects(Transaction.self).filter("transactionDate < '2015-09-30'")
However I get a exception:
Invalid operator type', reason: 'Operator '<' not supported for string type.
it I cannot use < operator for Strings. How can I search
these old strings? (so I am able to delete them)
You cannot use < for a string. Probably a better way is using Collection's filter method. You can pass a condition by a closure, then you can convert string object to Date object. Like the following:
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.dateFormat = "yyyy-MM-dd"
let filtered = realm.objects(Transaction.self)
.filter { formatter.date(from: $0.transactionDate)! < formatter.date(from: "2015-09-30")! }
Cons: It doesn't use Realm's native query, so Realm's lazy loading doesn't work. But it isn't a problem because I assume that this process is not called many times.

Swift CoreDate Fetch by Date and Time Stored as String and Sort

In CoreDate Table, I am storing Date and Time as String in below format
and I have a specific reason for storing as a string.
static let DateFmtVal = "dd-MM-yyyy";
static let TimeFmtVal = "hh:mm a";
Now while fetching I want to sort by converting the string to Date and Time and sort to display in TableView, so the row with newer ones come on top
let DateSortVar = NSSortDescriptor(key: DateCol, ascending: false)
let TimeSortVar = NSSortDescriptor(key: TimeCol, ascending: false)
fetchedResultsController.fetchRequest.sortDescriptors = [DateSortVar, TimeSortVar]
In Android, Using SQLite I got the solution as :
SqlQryVar = "SELECT * FROM " + NamTblVal +
" ORDER BY STRFTIME('%d-%m-%Y %h:%i'," + DateCol + "|| ' ' ||" + TimeCol + ") DESC;";
So kindly help me to do the same in Swift
You can add a computed property of type Date to your CoreData object (In your subclass ), do your conversions there and pass only it to the sort descriptor and you will get the same behavior like in your Android app, BUT take into consideration that what is different in iOS from Android that converting String to Date in iOS is slow!!! And allocating several DateFormatters is expensive (But you can use one for all your conversions). So if you don't have a lot of dates then it could work for you , anyway if you want to get the right sorted data then you will have to convert at some point.
Another solution without converting to date is considering to use other format for the date column i.e "yyyy-mm-dd" then the String sort will match the date.

getting specific object in fetchedObjects without iteration

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.

Resources