如何在 Swift 中的协议练习中对齐 table 行和列

How to align the table rows and columns within exercise on Protocols in Swift

根据练习,在标题、作者或评分长于主 column/s 中的 header 后,table 变得未对齐(更正错误后:“重复计数应该non-negative" 通过将 - 更改为 + 以实现常量 paddingNeeded)。我想保持列宽与给定列中最长的字符串一样长,并调整其他单元格中的间距。我试图在有条件的 printTable 函数中解决它,但是没有设法比较 columnLabel 和 item,因为它们处于不同的循环中。还尝试在协议中设置新功能来管理对齐。想不通,请指教。

protocol TabularDataSource {
    var numberOfRows: Int { get }
    var numberOfColumns: Int { get }
    func label(forColumn column: Int) -> String
    func itemFor(row: Int, column: Int) -> String
}

func printTable(_ dataSource: TabularDataSource & CustomStringConvertible) {
    print("Tab. 1: \(dataSource)")

    var headerRow = "|"

    var columnWidths = [Int]()

    for ii in 0 ..< dataSource.numberOfColumns {
        let columnLabel = dataSource.label(forColumn: ii)
        let columnHeader = " \(columnLabel) |"
        headerRow += columnHeader
        columnWidths.append(columnLabel.count)
    }
    print(headerRow)

for ii in 0 ..< dataSource.numberOfRows {
    
    var out = "|"
   
        for jj in 0 ..< dataSource.numberOfColumns {
            let item = dataSource.itemFor(row: ii, column: jj)
            let paddingNeeded = columnWidths[jj] - item.count
            let padding = repeatElement(" ", count:   paddingNeeded).joined(separator: "")
            out += " \(padding)\(item) |"
        }
        print(out)
    }
}

struct Book {
    let title: String
    let author: String
    let rating: Int
}

struct BookCollection: TabularDataSource, CustomStringConvertible {
    let name: String
    var books = [Book]()

    var description: String {
        return ("\(name) book collection")
    }

    init(name: String) {
        self.name = name
    }

    mutating func add(_ book: Book) {
        books.append(book)
    }

    var numberOfRows: Int {
        return books.count
    }

    var numberOfColumns: Int {
        return 3
    }

    func label(forColumn column: Int) -> String {
        switch column {
        case 0: return "Title"
        case 1: return "Author"
        case 2: return "Rating"
        default: fatalError("Invalid column!")
        }
    }

    func itemFor(row: Int, column: Int) -> String {
        let book = books[row]
        switch column {
        case 0: return book.title
        case 1: return String(book.author)
        case 2: return String(book.rating)
        default: fatalError("Invalid column!")
        }
    }
}

var bookCollection = BookCollection(name: "Fantasy")
bookCollection.add(Book(title: "Ava", author: "Reno", rating: 7))
bookCollection.add(Book(title: "Vis", author: "Luc", rating: 7))
bookCollection.add(Book(title: "Te", author: "Julo", rating: 9))

printTable(bookCollection)

您可以向数据源添加一个方法来了解列的最大长度:

protocol TabularDataSource {
    func width(for column: Int) -> Int
}

实施:


func width(for column: Int) -> Int {
    var labels = (0..<numberOfRows).map { itemFor(row: [=11=], column: column )}
    labels.append(label(forColumn: column))
    let max = labels.max(by: { [=11=].count < .count })?.count ?? 0
    return max + 2 //space before/after
}

然后:

func printTable(_ dataSource: TabularDataSource & CustomStringConvertible) {
    var lines = "|"
    for aColumn in 0..<dataSource.numberOfColumns {
        let label = dataSource.label(forColumn: aColumn)
        let totalOfSpaces = dataSource.width(for: aColumn) - label.count
        let spaces = repeatElement(" ", count: totalOfSpaces / 2).joined(separator: "")
        let additionalSpace = totalOfSpaces % 2 == 0 ? "" : " "
        lines += "\(additionalSpace)\(spaces)\(label)\(spaces)|"
    }
    lines += "\n"
    for aRow in 0..<dataSource.numberOfRows {
        lines += "|"
        for aColumn in 0..<dataSource.numberOfColumns {
            let label = dataSource.itemFor(row: aRow, column: aColumn)
            let totalOfSpaces = dataSource.width(for: aColumn) - label.count
            let additionalSpace = totalOfSpaces % 2 == 0 ? "" : " "
            let spaces = repeatElement(" ", count: totalOfSpaces / 2).joined(separator: "")
            lines += "\(additionalSpace)\(spaces)\(label)\(spaces)|"
        }
        lines += "\n"
    }
    print(lines)
}

可以分解为:

func printTable(_ dataSource: TabularDataSource & CustomStringConvertible) {
    func text(text: String, for width: Int) -> String {
        let totalOfSpaces = width - text.count
        let spaces = repeatElement(" ", count: totalOfSpaces / 2).joined(separator: "")
        let additionalSpace = totalOfSpaces % 2 == 0 ? "" : " "
        return "\(additionalSpace)\(spaces)\(text)\(spaces)|"
    }
        lines = "|"
    for aColumn in 0..<dataSource.numberOfColumns {
        lines += text(text: dataSource.label(forColumn: aColumn), for: dataSource.width(for: aColumn))
    }
    var lines += "\n"
    for aRow in 0..<dataSource.numberOfRows {
        lines += "|"
        for aColumn in 0..<dataSource.numberOfColumns {
            lines += text(text: dataSource.itemFor(row: aRow, column: aColumn), for: dataSource.width(for: aColumn))
        }
        lines += "\n"
    }
    print(lines)
}