按日期|Swift发布表视图数据的排序顺序



本质上,我有一个使用JSON数据填充的表视图,该表视图包含使用JSON中的allowdate对数据进行分组的部分。

如下所示的allowdate包含一个日期,但没有以数字格式显示,而是看起来像:March 26th 2020,因此很难控制它在表视图中的显示顺序。

在函数fetchJSON中,我执行:

self.structure.sort { $1. allowdate < $0.allowdate }

但这并不正确,也没有将日期(例如1月(置于3月之上。

var sections = [TableSection]()
var structure = [TableStructure]()
private func fetchJSON() {
guard let url = URL(string: "(URL.url)example"),
let value = name.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "item1=(value)&item2=(value)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }

do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.structure.sort { $1. allowdate < $0.allowdate }
let res = try decoder.decode([TableStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0. allowdate })
let keys = grouped.keys.sorted()
self.sections = keys.map({TableSection(date: $0, items: grouped[$0]!)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print(error)
}
}.resume()
}

JSON:

[
{
"person": "Jack",
"allowdate": "March 26th 2020",
"ready_time": "10:00 am"
}
]

要解码此JSON,我使用以下结构:

struct TableSections {
let date : String
var items : [TableStructure]
}

struct TableStructure: Decodable {
let person: String
let allowdate: String
let ready_time: String
enum CodingKeys : String, CodingKey {
case person, allowdate, ready_time
}
}

检查此URL:

  • https://nsdateformatter.com

它将帮助您尝试不同的DateFormatter

现在,对于您的场景,尝试更改如下结构:

struct TableStructure: Decodable {
let person: String
let allowdate: Date?
let ready_time: String
enum CodingKeys : String, CodingKey {
case person, allowdate, ready_time
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.person = try values.decode(String.self, forKey: .person)
self.ready_time = try values.decode(String.self, forKey: .ready_time)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM d'th',yyyy"
let allowDateStringValue = try values.decode(String.self, forKey: .allowdate)
self.allowdate = dateFormatter.date(from: allowDateStringValue)
}
}

您可以将数组排序为:

structure.sort { (firstStructure, secondStructure) -> Bool in
if let firstDate = firstStructure.allowDate, let secondDate = secondStructure.allowDate {
return firstDate.timeIntervalSince1970 < secondDate.timeIntervalSince1970
}
return false
}

如果你不想更改struct格式,那么你可以这样做:

struct TableStructure: Decodable {
let person: String
let allowdate: String
let ready_time: String
enum CodingKeys : String, CodingKey {
case person, allowdate, ready_time
}
}

并且在排序时:

var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM d'th',yyyy"
structure.sort { (firstStructure, secondStructure) -> Bool in
if let firstDate = dateFormatter.date(from: firstStructure.allowDate),
let secondDate = dateFormatter.date(from: secondStructure.allowDate) {
return firstDate.timeIntervalSince1970 < secondDate.timeIntervalSince1970
}
return false
}

我在操场上试过这个。它似乎适用于不同的序数:

import UIKit
let json = """
[
{
"person": "Jack",
"allowdate": "March 26th 2020",
"ready_time": "10:00 am"
},
{
"person": "Jill",
"allowdate": "January 1st 2020",
"ready_time": "1:00 pm"
},
{
"person": "Don",
"allowdate": "January 10th 2020",
"ready_time": "1:25 pm"
}
]
"""
struct TableStructure: Decodable {
// These first three come frm the json
let person: String
let allowdate: String
let readyTime: String
// We'll calculate this one later
let compareDate: Date
// This date formatter will read in dates like "January 10th 2020"
// So to use this, we will need to pull the ordinal letters off first
static let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd yyyy"
return dateFormatter
}()
enum DecodeError: Error {
case compareDateError
}
enum CodingKeys: String, CodingKey {
case person
case allowdate
case readyTime
}
// We decode the three key/values that are passed down, and we calculate a date that we can use to compare
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
person = try values.decode(String.self, forKey: .person)
readyTime = try values.decode(String.self, forKey: .readyTime)
allowdate = try values.decode(String.self, forKey: .allowdate)
if let date = TableStructure.comparingDate(from: allowdate) {
compareDate = date
} else {
throw DecodeError.compareDateError
}
}
// We pull the ordinal letters off of the date, and are left with something cleaner
// A regex could make this much simpler, but me and regex's, we don't get along so great
static func comparingDate(from dateString: String) -> Date? {
var datePurgedArray = dateString.split(separator: " ")
if datePurgedArray.count == 3 {
datePurgedArray[1] = datePurgedArray[1].filter("0123456789.".contains)
let newDateString = datePurgedArray.joined(separator:" ")
print(newDateString)
return TableStructure.dateFormatter.date(from: newDateString)
} else {
return nil
}
}
}
if let jsonData = json.data(using: .utf8) {
print("json Data is (jsonData)")
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let array = try decoder.decode([TableStructure].self, from: jsonData).sorted { $0.compareDate < $1.compareDate }
print("array is (array)")
} catch {
print(error.localizedDescription + "could not decode array")
}
} else {
print("could not get data from json")
}

字符串日期格式无法可靠排序。

您必须将日期字符串解码为Date。这就是日期解码策略的设计目的。

有一个问题:DateFormatter不支持像1st这样的文字顺序日期字符串,您必须删除stndrdth。最有效的方法是replacingOccurrencesregularExpression选项。

声明结构(再次不需要CodingKeys,并将结构成员命名为camelBased(

struct TableStructure: Decodable {
let person: String
let allowdate: Date
let readyTime: String
}

并添加策略

do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .custom({ decoder -> Date in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
let trimmedString = dateString.replacingOccurrences(of: "(st|nd|rd|th) ", with: " ", options: .regularExpression)
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "MMMM dd yyyy"
guard let result = formatter.date(from: trimmedString) else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "Wrong date format")}
return result
})
self.structure.sort { $1. allowdate < $0.allowdate }
let res = try decoder.decode([TableStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0. allowdate })
let keys = grouped.keys.sorted()
self.sections = keys.map({TableSection(date: $0, items: grouped[$0]!)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}

假设Date是使用默认日期格式打印的。如果你需要自定义日期格式,你必须添加第二个日期格式

最新更新