Here is the problem:
My API returns a list of monthly transactions, I have to grab that result, divide it into sections to populate a tableView where each section is a month with its respective transactions.
Here is the Code
var sectionTest = Dictionary<String, Array<TableSections>>()
var sortedSections: [String] = []
for entry in entries {
if entry.type != "S" {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy/MM/dd"
if let date = dateFormatter.date(from: entry.date) {
dateFormatter.dateFormat = "MMMM"
dateFormatter.locale = NSLocale(localeIdentifier: "pt_BR") as Locale!
let sectionMonth = dateFormatter.string(from: date)
if self.sectionTest.index(forKey: sectionMonth) == nil {
self.sectionTest[sectionMonth] = [TableSections(month: sectionMonth, description: entry.description, value: entry.value, date: entry.date, type: entry.type)]
} else {
self.sectionTest[sectionMonth]?.append(TableSections(month: sectionMonth, description: entry.description, value: entry.value, date: entry.date, type: entry.type))
}
self.sortedSections = self.sectionTest.keys.sorted(by: >)
Here is where I'm stuck
self.sortedSections returns an array sorted by alphabetical order, in this case if I request for the last 60 days of transactions it will return an array with:
print(sortedSections)
["november", "january", "december"]
Related
I have a list of arrays that contains date object along with time, I have a record like in the this date format yyyy-MM-dd'T'HH:mm:ssZ I want to filter my array with ascending order and I also want to store unique values as in dates.
I am having record like 2021-04-01T11:00:00-04:00, 2021-04-01T12:00:00-04:00, 2021-04-02T09:00:00-04:00, 2021-04-02T10:00:00-04:00 and what I want 2021-04-01T11:00:00-04:00, 2021-04-02T09:00:00-04:00
Means before time value but only once. I have used this code but not working
let totalPods = [Items]
self.totalPods.sort { (firstItem, secondItem) -> Bool in
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" // "2021-04-01T11:00:00-04:00"
let firstDate = dateFormatter.date(from: firstItem.startDateString)
let secondDate = dateFormatter.date(from: secondItem.startDateString)
if let safeSecondDate = secondDate {
return firstDate?.compare(safeSecondDate) == .orderedAscending
}
return false
}
struct Items: Hashable {
let startDateString: String
}
let totalPods: [Items] = [Items(startDateString: "2021-04-01T11:00:00-04:00"),
Items(startDateString: "2021-04-01T12:00:00-04:00"),
Items(startDateString: "2021-04-02T09:00:00-04:00"),
Items(startDateString: "2021-04-02T10:00:00-04:00"),
Items(startDateString: "2021-04-01T11:00:00-04:00"),
Items(startDateString: "2021-04-01T11:00:00-04:00")]
// Store unique values
let uniqs = Array(Set(totalPods))
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
// filter by ASC
let sorted = uniqs.sorted { (item1, item2) in
dateFormatter.date(from: item1.startDateString)!.compare(dateFormatter.date(from: item2.startDateString)!) == .orderedAscending
}
// You can extend your model
extension Items {
var date: Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
return dateFormatter.date(from: startDateString)!
}
}
// And sort like this
let sorted = uniqs.sorted { (item1, item2) in
item1.date.compare(item2.date) == .orderedAscending
}
I want to group data by dates which has initial format of yyyy-MM-dd'T'HH:mm:ss.SSSZ and display as E, dd MMM. I have managed to group them by the dates but failed to sort the dates in a descending order. How do I sort the date components after grouping in Dictionary?
JSON Response
{
"list": [
{
"userId": "test1",
"transactionTime": "2019-06-20T14:01:00.253+08:00"
},
{
"userId": "test2",
"transactionTime": "2019-06-16T14:02:00.253+08:00"
},
{
"userId": "test3",
"transactionTime": "2019-06-12T14:01:00.253+08:00"
},
{
"userId": "tes4",
"transactionTime": "2019-06-17T14:02:00.253+08:00"
},
]
}
Grouping
func convertToDateObj() -> Date {
let dateFormatter = DateFormatter()
// Convert from initial date string to date object
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let dateObj = dateFormatter.date(from: self)!
return dateObj
}
// Group list by date
let groupedList = Dictionary(grouping: rawTransactionsList, by: { list -> DateComponents in
let dateObj = list.transactionTime.convertToDateObj()
let date = Calendar.current.dateComponents([.day, .month], from: (dateObj))
return date
})
// QUESTION
// How do I sort the keys?
// groupList.keys.sorted(...)?
// Populate my list with grouped list
groupedList.keys.forEach { key in
print("Group keys \(key)")
let values = groupedList[key]
groupedtransactionsList.append(values ?? [])
}
You should probably also group by the year, otherwise the same day in different years will be in the same group:
let date = Calendar.current.dateComponents([.day, .month, .year], from: (dateObj))
One way to sort the keys is to convert the date components back into Dates, using Calendar.current.date(from:):
let sortedList = groupedList.sorted {
Calendar.current.date(from: $0.key) ?? Date.distantFuture <
Calendar.current.date(from: $1.key) ?? Date.distantFuture
}
sortedList.forEach { key, values in
print("Group keys \(key)")
groupedtransactionsList.append(values ?? [])
}
My suggestion is to use a date string with format yyyy-MM-dd as dictionary key rather than date components. This string can be sorted.
let groupedList = Dictionary(grouping: rawTransactionsList, by: { list -> String in
let dateObj = list.transactionTime.convertToDateObj()
let comps = Calendar.current.dateComponents([.day, .month, .year], from: dateObj)
return String(format: "%ld-%.2ld-%.2ld", comps.year!, comps.month!, comps.day!)
})
groupedtransactionsList = groupedList.keys.sorted(by: >).map { groupedList[$0]! }
I have an array of dates like this :-
var dateArray = ["2016-04-20", "2016-04-22", "2016-04-25", "2016-04-30"]
and I want to find out the difference of days between them. I do some research and i am able to do that with only two dates here is the approach i did in finding the difference between two date
let dateFormatter = DateFormatter()
let isoDate = "2016-04-20"
let calendar = NSCalendar.current
let currentDate = Date()
And in my viewDidLoad method I did this
override func viewDidLoad() {
super.viewDidLoad()
// let components = calendar.dateComponents([.day], from: )
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.locale = Locale(identifier: "en_US_POSIX") //en_US_POSIX
let formatedStartDate = dateFormatter.date(from: isoDate)
let date = dateArray.compactMap { dateFormatter.date(from: $0) } // for date array
print(date)
let components = Set<Calendar.Component>([.day])
let differenceOfDate = Calendar.current.dateComponents(components, from: formatedStartDate!, to: currentDate )
print (differenceOfDate)
apiData()
}
As you can see in the code i created a constant let isoDate = "2016-04-20" and changed into formatedStartDate and find the difference between form this date to current date then it worked. But what if I have my own array of dates and how can i find the difference of my own array of dates and sort it into increasing or decreasing order. Please help?
Create the date formatter
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.locale = Locale(identifier: "en_US_POSIX") //en_US_POSIX
Map the date string array to Date instances
let dateStringArray = ["2016-04-20", "2016-04-22", "2016-04-25", "2016-04-30"]
let dateArray = dateStringArray.map {dateFormatter.date(from: $0)!} // add .sorted() if the array is unordered.
In a loop get the differences between adjacent items
var differences = [Int]()
for i in 0..<dateArray.count - 1 {
let dayComponent = Calendar.current.dateComponents([.day], from: dateArray[i], to: dateArray[i+1])
differences.append(dayComponent.day!)
}
print(differences)
I'm not sure if you only want to compare the dates next to each other or compare each and every date. In the first case go with vadians solution. In the other case you can do something like this:
let dateStrings = ["2016-04-20", "2016-04-22", "2016-04-25", "2016-04-30"]
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
let dates = dateStrings.compactMap { dateFormatter.date(from: $0) }
let calendar = Calendar.current
var differences: [Int] = []
for i in 0..<dates.count {
for j in i + 1..<dates.count {
if let difference = calendar.dateComponents([.day], from: dates[i], to: dates[j]).day {
differences.append(difference)
}
}
}
let descendingDifferences = differences.sorted(by: >)
print(descendingDifferences) // results in [10, 8, 5, 5, 3, 2]
I have Date() properties. startingAt and endingAt. And an array of Date(), which are alreadyRegistred. I have to create an array of strings with dates between startingAt and endingAt. StartingAt and endingAt are included and the last requirement is to exclude alreadyRegistred dates.
Do you have some elegant idea, how to do it? Thanks for help!
Edit: Maximum number of dates in final array will be about 7 days.
Dont forget that a Date is basically just a timestamp, and that you can have access to the addingTimeInterval(_:) method.
Knowing that, is very easy to do some calculation between two dates.
I do not have the whole knowledge about your required business logic, but here is a naive implementation that generates Dates between two dates. I'm sure you can run it in a playground and explore a little bit.
import UIKit
func intervalDates(from startingDate:Date, to endDate:Date, with interval:TimeInterval) -> [Date] {
guard interval > 0 else { return [] }
var dates:[Date] = []
var currentDate = startingDate
while currentDate <= endDate {
currentDate = currentDate.addingTimeInterval(interval)
dates.append(currentDate)
}
return dates
}
let startingDate = Date() // now
let endDate = Date(timeIntervalSinceNow: 3600 * 24 * 7) // one week from now
let intervalBetweenDates:TimeInterval = 3600 * 3// three hours
let dates:[Date] = intervalDates(from: startingDate, to: endDate, with: intervalBetweenDates)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .long
let dateStrings = dates.map{dateFormatter.string(from: $0)}
print("NOW : \(startingDate)")
for (index, string) in dateStrings.enumerated() {
print("\(index) : \(string)")
}
print("END DATE : \(endDate)")
Try this and see:
// Start & End date string
let startingAt = "01/01/2018"
let endingAt = "08/03/2018"
// Sample date formatter
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
// start and end date object from string dates
var startDate = dateFormatter.date(from: startingAt) ?? Date()
let endDate = dateFormatter.date(from: endingAt) ?? Date()
// String date array, to be excluded
let alreadyRegistred = ["01/01/2018", "15/01/2018", "10/02/2018", "20/02/2018", "05/03/2018"]
// Actual operational logic
var dateRange: [String] = []
while startDate <= endDate {
let stringDate = dateFormatter.string(from: startDate)
startDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate) ?? Date()
if (alreadyRegistred.contains(stringDate)) {
continue
} else {
dateRange.append(stringDate)
}
}
print("Resulting Array - \(dateRange)")
Here is result:
Resulting Array - ["02/01/2018", "03/01/2018", "04/01/2018", "05/01/2018", "06/01/2018", "07/01/2018", "08/01/2018", "09/01/2018", "10/01/2018", "11/01/2018", "12/01/2018", "13/01/2018", "14/01/2018", "16/01/2018", "17/01/2018", "18/01/2018", "19/01/2018", "20/01/2018", "21/01/2018", "22/01/2018", "23/01/2018", "24/01/2018", "25/01/2018", "26/01/2018", "27/01/2018", "28/01/2018", "29/01/2018", "30/01/2018", "31/01/2018", "01/02/2018", "02/02/2018", "03/02/2018", "04/02/2018", "05/02/2018", "06/02/2018", "07/02/2018", "08/02/2018", "09/02/2018", "11/02/2018", "12/02/2018", "13/02/2018", "14/02/2018", "15/02/2018", "16/02/2018", "17/02/2018", "18/02/2018", "19/02/2018", "21/02/2018", "22/02/2018", "23/02/2018", "24/02/2018", "25/02/2018", "26/02/2018", "27/02/2018", "28/02/2018", "01/03/2018", "02/03/2018", "03/03/2018", "04/03/2018", "06/03/2018", "07/03/2018", "08/03/2018"]
let startDate = Date()
let endDate = Date().addingTimeInterval(24*60*60*10) // i did this to get the end date for now
var stringdateArray = [String]()
if let days = getNumberofDays(date1: startDate, date2: endDate) {
for i in 0...days-1 {
let date = startDate.addingTimeInterval(Double(i)*24*3600)
let stringDate = getStringDate(fromDate: date, havingFormat: "yyyy-MM-dd")
if !(alreadyRegisteredArray.contains(stringDate)) { // checking if already registered
stringdateArray.append(stringDate)
}
}
}
and our helper method
let dateFormatter = DateFormatter()
func getStringDate(fromDate: Date,havingFormat: String) -> String {
dateFormatter.dateFormat = havingFormat
dateFormatter.amSymbol = "AM"
dateFormatter.pmSymbol = "PM"
let date = dateFormatter.string(from: fromDate)
return date
}
func getNumberofDays(date1: Date, date2: Date) -> Int? {
let calendar = NSCalendar.current
let date1 = calendar.startOfDay(for: date1)
let date2 = calendar.startOfDay(for: date2)
let components = calendar.dateComponents([.day], from: date1, to: date2)
return components.day
}
I'm using NSSortDescriptors to sort an Array by the date element. The date is set as String using a formatter which formats in the style : "dd/MM/yy HH:mm". This date element is stored inside dictionaries which are all stored in the array. Parts of my code for this are below:
// Date Formatting
let currentTime = Date()
let timeFormatter = DateFormatter()
timeFormatter.locale = Locale.current
timeFormatter.dateFormat = "HH:mm dd/MM/yy"
let convertedTime:String! = timeFormatter.string(from: currentTime)
// Descriptor
let descriptorD: NSSortDescriptor = NSSortDescriptor(key: "Date", ascending: false)
// Dictionary
let newUserRecord = [
"Name" : enteredName!,
"Score" : self.gameScore,
"Date" : convertedTime
] as [String : Any]
// Sorting
newUserArray.sort(using: [descriptorD])
However my problem is the date is only being sorted by the time (HH:mm) and not taking in account the (dd/MM/yy) part. For example if I sort by the date and have a date of 13:12 19/11/16 and a date of 09:12 18/11/16 the 09:12 date will appear first even though it should be the 13:12 as it is a day later. How do I fix this?
This is the object oriented Swift way:
Declare a struct rather than a dictionary and include the time formatter
struct User {
let timeFormatter : DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale.current
formatter.dateFormat = "HH:mm dd/MM/yy"
return formatter
}()
let name : String
let score : Int
let time : Date
var convertedTime : String {
return timeFormatter.string(from: time)
}
}
Declare an array of the User type and add two instances
var newUserArray = [User]()
newUserArray.append(User(name: "Foo", score: 12, time: Date().addingTimeInterval(1000.0)))
newUserArray.append(User(name: "Bar", score: 78, time: Date()))
Sort the array by time descending
newUserArray.sort(by: {$0.time > $1.time })
And print the formatted date
print(newUserArray[0].convertedTime)