我有一个包含 4 条评论的列表,我想根据回复和日期对其进行排序。
let comment1 = Comment(content: "Comment1". date: "12daysAgo", subCommentOf: nil)
let comment2 = Comment(content: "Comment2". date: "5daysAgo", subCommentOf: nil)
let comment3 = Comment(content: "Comment3". date: "10daysAgo", subCommentOf: comment2)
let comment4 = Comment(content: "Comment4". date: "1daysAgo", subCommentOf: comment1)
let comment5 = Comment(content: "Comment5". date: "10daysAgo", subCommentOf: comment1)
所以排序后的最终结果应该是 tableview 中的 5 个单元格 哪里
comment1 1st cell -> comment5 -> comment4 -> comment2 -> comment3
Comment 是一个模型,其中 subComment 是 Comment 可选的
我已经试过了
let sortResults = thisComments.sorted {
let main0 = ($0.isSubComment != nil) ? 0 : 1
let main1 = ($1.isSubComment != nil) ? 0 : 1
if main0 < main1 {
return main0 < main1
}
return false
}
最佳答案
如果你想像这样订购你的 Comment
对象,我会添加一个 before
方法(或任何你想调用它的方法)到 Comment
看看它是否应该在另一个 Comment
之前或之后显示一个 Comment
:
class Comment {
let content: String
let date: String
let parent: Comment?
init(content: String, date: String, parent: Comment?) {
self.content = content
self.date = date
self.parent = parent
}
/// Determine order of comments
///
/// - Parameters:
/// - other: The other `Comment` I'm going to compare myself to.
/// - order: Whether I want to compare the numeric field in dates in ascending or
/// descending order (default, descending; i.e. older ones first)
///
/// - Returns: Return `true` if this should be sorted before `other`.
func before(_ other: Comment, dateOrder order: ComparisonResult = .orderedDescending) -> Bool {
switch (parent, other.parent) {
case (nil, nil):
// if we are both top level comments), let's just compare our dates;
// note use of `.numeric` option (to sort these strings in "1", "5", "10", rather than "1", "10", "5")
return date.compare(other.date, options: .numeric, range: nil, locale: nil) == order
case (let selfParent?, let otherParent?) where selfParent === otherParent:
// if we are siblings, let's just compare our dates;
// again, note use of `.numeric` option
return date.compare(other.date, options: .numeric, range: nil, locale: nil) == order
case (nil, _):
// if I'm a top-level comment, but other comment isn't ...
// ... and this is my child, then I'm obviously before it
// ... otherwise let's see if I'm before the other comment's parent or not
if self === other.parent! {
return true
} else {
return before(other.parent!, dateOrder: order)
}
case (_, nil):
// if I'm not top level comment, but other one is ...
// ... and I'm a child of the other one, then I'm obviously after it
// ... otherwise see if my parent is before that other comment
if parent! === other {
return false
} else {
return parent!.before(other, dateOrder: order)
}
default:
// if we got there, we're apparently children of different parents,
// so let's compare our respective parents.
return parent!.before(other.parent!, dateOrder: order)
}
}
}
// useful extension if I want to `print` my `Comment` objects
extension Comment: CustomStringConvertible {
var description: String { return "<Comment; content=\(content); date=\(date); parent=\(parent?.content ?? "none")>" }
}
(请原谅我将 subCommentOf
属性重命名为更常见的名称,例如 parent
。当我试图捕获 parent/Comment
实例之间的子关系。)
无论如何,您可以执行以下操作:
let comment1 = Comment(content: "Comment1", date: "12daysAgo", parent: nil)
let comment2 = Comment(content: "Comment2", date: "5daysAgo", parent: nil)
let comment3 = Comment(content: "Comment3", date: "2daysAgo", parent: comment2)
let comment4 = Comment(content: "Comment4", date: "1daysAgo", parent: comment1)
let comment5 = Comment(content: "Comment5", date: "10daysAgo", parent: comment1)
let comments = [comment1, comment2, comment3, comment4, comment5]
.sorted { $0.before($1) }
结果是:
"<Comment; content=Comment1; date=12daysAgo; parent=none>",
"<Comment; content=Comment5; date=10daysAgo; parent=Comment1>",
"<Comment; content=Comment4; date=1daysAgo; parent=Comment1>",
"<Comment; content=Comment2; date=5daysAgo; parent=none>",
"<Comment; content=Comment3; date=2daysAgo; parent=Comment2>"
或者,如果您希望它们按升序排序:
let comments = [comment1, comment2, comment3, comment4, comment5]
.sorted { $0.before($1, dateOrder: .orderedAscending) }
产生:
"<Comment; content=Comment2; date=5daysAgo; parent=none>",
"<Comment; content=Comment3; date=2daysAgo; parent=Comment2>",
"<Comment; content=Comment1; date=12daysAgo; parent=none>",
"<Comment; content=Comment4; date=1daysAgo; parent=Comment1>",
"<Comment; content=Comment5; date=10daysAgo; parent=Comment1>"
就我个人而言,我认为上面是一团糟(它不适用于任意深度的评论和回复树;要让它在那种情况下起作用,我们必须让它变得更加困惑)并且我宁愿:
我会采用相反的方法,让评论跟踪他们的回复。
顺便说一句,我发现将日期表示为“10 天前”有点令人不适,因为“它多大了”是关于如何显示评论创建日期的函数,而不是属性本身。我会将
date
设为Date
类型。如果您需要它,我会添加一个init
方法,它可以将例如 RFC 3999/ISO 8601 日期字符串转换为日期。我还会添加一个很好的格式化程序,根据日期创建howOld
字符串,以便在您的 UI 中显示它。
例如:
class Comment {
let content: String
let date: Date
var replies: [Comment]?
private static var dateFormatter = ISO8601DateFormatter()
private static var howOldFormatter: DateComponentsFormatter = {
let _formatter = DateComponentsFormatter()
_formatter.unitsStyle = .full
_formatter.maximumUnitCount = 2
_formatter.allowedUnits = [.year, .month, .day, .hour, .minute, .second]
return _formatter
}()
init(content: String, date: Date, replies: [Comment]?) {
self.content = content
self.date = date
self.replies = replies
}
init(content: String, date: String, replies: [Comment]?) {
self.content = content
self.date = Comment.dateFormatter.date(from: date)!
self.replies = replies
}
var howOld: String { return Comment.howOldFormatter.string(from: date, to: Date())! }
}
然后“给我所有评论和回复(和子回复)的排序列表”变成了一个相当简单的递归函数:
extension Comment {
func isOrdered(_ rhs: Comment, order: ComparisonResult = .orderedAscending) -> Bool {
return date.compare(rhs.date) == order
}
}
extension Array where Element == Comment {
/// Return simple recursively flatten array
///
/// - Parameter order: The order you want them sorted. If not provided, it defaults to ascending order.
/// - Returns: The recursively flattened array
func flatten(order: ComparisonResult = .orderedAscending) -> [Comment] {
return sorted() { $0.isOrdered($1, order: order) }
.flatMap { comment -> [Comment] in
if let replies = comment.replies {
return [comment] + replies.flatten(order: order)
} else {
return [comment]
}
}
}
}
或者,如果您想跟踪每个回复(和子回复)的缩进级别:
extension Array where Element == Comment {
/// Return recursively flattened array of tuples, (level, comment), where
///
/// - Parameters:
/// - order: The order you want them sorted. If not provided, it defaults to ascending order.
/// - level: The level to be used for top level objects. Generally you don't have to provide this.
/// - Returns: The recursively flattened array of tuples, `(Int, Comment)`, where the `Int` is the level of indentation.
func flattenTree(order: ComparisonResult = .orderedAscending, level: Int = 0) -> [(Int, Comment)] {
return sorted() { $0.isOrdered($1, order: order) }
.flatMap { comment -> [(Int, Comment)] in
if let replies = comment.replies {
return [(level, comment)] + replies.flattenTree(order: order, level: level + 1)
} else {
return [(level, comment)]
}
}
}
}
如果我这样做:
let reply0 = Comment(content: "Comment6", date: "2017-11-04T00:00:00Z", replies: nil)
let reply1 = Comment(content: "Comment4", date: "2017-11-02T00:00:00Z", replies: nil)
let reply2 = Comment(content: "Comment5", date: "2017-11-01T00:00:00Z", replies: [reply0])
let comment1 = Comment(content: "Comment1", date: "2017-10-27T00:00:00Z", replies: [reply1, reply2])
let reply3 = Comment(content: "Comment3", date: "2017-10-01T00:00:00Z", replies: nil)
let comment2 = Comment(content: "Comment2", date: "2017-09-14T00:00:00Z", replies: [reply3])
let comments = [comment1, comment2]
.flattenTree(order: .orderedAscending)
.map { ($0.0, $0.1.content, $0.1.howOld) }
print(comments)
那会报告(在我运行这个的时候):
[(0, "Comment2", "1 month, 26 days"),
(1, "Comment3", "1 month, 9 days"),
(0, "Comment1", "12 days, 18 hours"),
(1, "Comment5", "7 days, 18 hours"),
(2, "Comment6", "4 days, 18 hours"),
(1, "Comment4", "6 days, 18 hours")]
关于swift - 多类型排序swift 4,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47145101/