A Quick Swift Script Yak Shave

[Updated Version with downloadable OS X service]
In the process of writing a longer post yesterday, I wanted to add a markdown table, and was once again annoyed at the mild PITA that it is to use pipe characters and then the formatting characters a lá:

|    title    |   title2   |  
| :---------: | :--------: |  
|  entry one  |  entry two |  
| entry three | entry four |  

Being aware of Brett Terpstra’s excellent Markdown Services that have a table cleanup function, but actually writing the da** tables is kind of annoying, so I in classic Yak-Shaving fashion, I decided to write a simple Swift script that would turn the following into the format above. Then make that into an OS X service.

title, title2
entry one, entry two
entry three, entry four

It really wasn’t that tricky, and probably would have been at least as easy using python or ruby, but ¯_(ツ)_/¯. (<– typed using textexpander).

It’s written in Swift 3. I should use stdin and stdout, but the syntax for NSFileHandler changed enough, and I know so little of it, that I decided to punt on that, as I didn’t need the Yak that well shaven. Fortunately the readline function in a while loop works just fine and simply print(output) also works. Likewise the code is naive, could be more functional, does not handle many (any) exceptional cases or check for consistency, but again, it does the job for a simple case. (You know, I’m embarrassed for anyone to actually look at my code)

Likewise, I would like to add a small tweak so that I can optionally write it this way to add the alignment more easily:

title, title2, title3
>, -, <
entry one, entry two, entry three
entry four, entry five, entry six

that would translate the second line to:

| ---: | :---: | :--- |

But Yak-Shaving is done for now.

Entire script is here:

#!/usr/bin/swift

//
//  main.swift
//  CSVToTables
//
//  Created by Christopher Brandow on 7/12/16.
//  Copyright © 2016 flouu. All rights reserved.
//


import Foundation

func titleAndEntryFrom(string: String) -> (String, [String])? {
    var lines = string.characters.split{$0 == "\n"}.map(String.init)

    guard lines.count > 1, let titleLine = lines.first else {
        return nil
    }

    lines.removeFirst()
    return (titleLine, lines)

}

func markdownTableFromTitleAndEntries(title: String, body: [String]) -> String? {

    let titleMarkdown = markdownFrom(line: title)
    let entries = title.characters.split{$0 == ","}.map(String.init)

    var separatorMarkdown = "\n|"
    for _ in 0..<entries.count {
        separatorMarkdown += " :---: |"
    }
    var bodyMarkdown = ""

    for line in body {
        bodyMarkdown += markdownFrom(line: line)
    }

    return titleMarkdown + separatorMarkdown + bodyMarkdown
}

func markdownFrom(line: String) -> String {
    var markdown = "\n| "
    let entries = line.characters.split{$0 == ","}.map(String.init)

    for entry in entries {
        markdown += entry
        markdown += " |"
    }
    return markdown
}

var string = ""

while let thing = readLine(strippingNewline: false) {
    string += thing
}

if let titleAndBody = titleAndEntryFrom(string: string) {
    if let output = markdownTableFromTitleAndEntries(title: titleAndBody.0, body: titleAndBody.1) {
        print(output)
    }
}
 
19
Kudos
 
19
Kudos

Now read this

Partial vs. Complete Functions

Whoo boy, stick with this seemingly complicated article (linked in the title), because it directly addresses that subtle issue that I face regularly when functions or methods that I write can’t properly map directly all input to your... Continue →