Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
csv to multimarkdown
#!/usr/bin/swift -suppress-warnings
// based on https://chrisbrandow.svbtle.com/csv-to-multimarkdown-tables
import Foundation
let separators: [Character: String] = ["=": ":---:", ">": "----:", "<": ":----"]
let separatorSet = CharacterSet(charactersIn: "><=, ")
func csv(data: String) -> [[String]] {
var result: [[String]] = []
let rows = data.components(separatedBy: "\n")
for row in rows {
if row.count > 0{
let columns = row.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: ",")
result.append(columns)
}
}
return result
}
func titleAndEntriesFrom(string: String) -> (String, String?, [String])? {
var lines = string.components(separatedBy: "\n")
guard lines.count > 1, let titleLine = lines.first else {
print("need more than one line ->")
return nil
}
lines.removeFirst()
let separatorMarkdown: String?
if let second = lines.first, CharacterSet(charactersIn: second).isSubset(of: separatorSet) {
separatorMarkdown = separatorFrom(line: second)
lines.removeFirst()
} else {
separatorMarkdown = nil
}
return (titleLine, separatorMarkdown, lines)
}
func max_by_row(data: [[String]]) -> [Int] {
let columns = data[0].count
// min length is 5 ie :---:
var the_max = Array(repeating: 5, count: columns)
for row in data {
guard row.count == columns else{
continue; // TODO what if empty cells?
}
for col in 0..<columns {
the_max[col] = max(the_max[col], row[col].count)
}
}
return the_max
}
func max_by_row(val1: [Int], val2: [Int]) -> [Int] {
var res = val1
for i in 0..<val1.count {
res[i] = max(val1[i], val2[i])
}
return res
}
func mdTableFrom(components: (String, String?, [String])) -> String? {
let titles = csv(data:components.0)
let body = csv(data:components.2.joined(separator: "\n"))
let maxLength = max_by_row(val1: max_by_row(data: titles), val2: max_by_row(data: body))
var titleMarkdown = ""
var bodyMarkdown = ""
var separatorMarkdown = ""
for i in 0..<maxLength.count {
titleMarkdown += " | " + (titles[0][i] as NSString).padding(toLength:maxLength[i], withPad: " ", startingAt: 0)
}
titleMarkdown += " |\n"
for i in 0..<maxLength.count {
separatorMarkdown += " | :" + "".padding(toLength:maxLength[i]-2, withPad: "-", startingAt: 0) + ":"
}
separatorMarkdown += " |\n"
for j in 0..<body.count {
for i in 0..<maxLength.count {
if i < body[j].count{
bodyMarkdown += " | " + (body[j][i] as NSString).padding(toLength:maxLength[i], withPad: " ", startingAt: 0)
}
}
bodyMarkdown += " |\n"
}
//let titleMarkdown = markdownFrom(line: components.0)
//let titleEntries = components.0.components(separatedBy: ",")
//let separatorMarkdown = components.1 ?? titleEntries.reduce("\n| "){prev, sep in "\(prev) :---: | "}
//let bodyMarkdown = components.2.reduce(""){prev, line in "\(prev) \(markdownFrom(line: line))"}
return titleMarkdown + separatorMarkdown + bodyMarkdown
}
func separatorFrom(line: String) -> String {
let entries = line.components(separatedBy: ",")
let things = entries.flatMap({$0.trimmingCharacters(in: CharacterSet.whitespaces).characters.first})
let markdown = things.reduce("\n|"){prev, entry in "\(prev) \(separators[entry] ?? ":xxx:") |"}
return markdown
}
func markdownFrom(line: String) -> String {
guard line.characters.count > 0 else {
return "\n"
}
let entries = line.components(separatedBy: ",")
return entries.reduce("\n| "){prev, entry in "\(prev) \(entry.trimmingCharacters(in: CharacterSet.whitespaces)) |"}
}
var string = ""
while let thing = readLine(strippingNewline: false) {
string += thing
}
if let titleAndBody = titleAndEntriesFrom(string: string), let output = mdTableFrom(components: titleAndBody) {
print(output)
} else {
print(string)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment