swift - 多类型排序swift 4

标签 swift

我有一个包含 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/

相关文章:

ios - 以编程方式更改根ViewController

ios - Swift 全屏导航推送 Segue

swift - alamofire swift : Can't download image to location specified by user

ios - 从 CoreData 获取返回 AnyObject

swift - 不安全的可变寻址器崩溃

ios - 是否可以横向旋转 UIAlertController(和 Alert)?

ios - 类型 'AnyObject' 不符合协议(protocol) 'BooleanType'

swift - 鼠标移动和 ViewController

swift - 如何使用 Swift-Html-Parser 获取图片 src

ios - 将图像置于按钮中会产生意想不到的结果