Aaron Brager Aaron Brager - 2 months ago 8
Swift Question

Format println output in a table

Like this Java question, but for Swift.

How can I output a table like this to the console, ideally using

println
?

n result1 result2 time1 time2
-----------------------------------------------------
5 1000.00 20000.0 1000ms 1250ms
5 1000.00 20000.0 1000ms 1250ms
5 1000.00 20000.0 1000ms 1250ms


I tried using
println("n\tresult1\tresult2")
but the results don't line up properly.

Answer

I found a quick and easy way to generate columnar text output in Swift (3.0) using the String method "padding(::)" [In Swift 2.x, the method is named "stringByPaddingToLength(::)"]. It allows you to specify the width of your column, the text you want to use as a pad, and the index of the pad to start with. Works like a charm if you don't mind that it only works with left-aligned text columns. If you want other alignments, you have to buy into the other methods of character counting and other such complexities.

The solution below is contrived to illustrate the utility of the method "padding(::)". Obviously, the best way to leverage this would be to create a function that iterated through a collection to produce the desired table while minimizing code repetition. I did it this way to focus on the task at hand.

Lastly, "println" doesn't seem to exist in Swift 2.x+, so I reverted to "print()".

To illustrate an example using your stated problem:

//Set up the data
let n : Int = 5
let result1 = 1000.0
let result2 = 20000.0
let time1 = "1000ms"
let time2 = "1250ms"

//Establish column widths
let column1PadLength = 8
let columnDefaultPadLength = 12

//Define the header string
let headerString = "n".padding(toLength: column1PadLength, withPad: " ", startingAt: 0) + "result1".padding(toLength: columnDefaultPadLength, withPad: " ", startingAt: 0) + "result2".padding(toLength: columnDefaultPadLength, withPad: " ", startingAt: 0) + "time1".padding(toLength: columnDefaultPadLength, withPad: " ", startingAt: 0) + "time2".padding(toLength: columnDefaultPadLength, withPad: " ", startingAt: 0)

//Define the line separator
let lineString = "".padding(toLength: headerString.characters.count, withPad: "-", startingAt: 0)

//Define the string to display a line of our data
let nString = String(n)
let result1String = String(result1)
let result2String = String(result2)
let dataString = nString.padding(toLength: column1PadLength, withPad: " ", startingAt: 0) + result1String.padding(toLength: columnDefaultPadLength, withPad: " ", startingAt: 0) + result2String.padding(toLength: columnDefaultPadLength, withPad: " ", startingAt: 0) + time1.padding(toLength: columnDefaultPadLength, withPad: " ", startingAt: 0) + time2.padding(toLength: columnDefaultPadLength, withPad: " ", startingAt: 0)

//Print out the data table
print("\(headerString)\n\(lineString)\n\(dataString)")

The output will be printed to your console in a tidy columnar format:

n       result1     result2     time1       time2       
--------------------------------------------------------
5       1000.0      20000.0     1000ms      1250ms  

Changing the variable "columnDefaultPadLength" from 12 to 8 will result in the following output:

n       result1 result2 time1   time2   
----------------------------------------
5       1000.0  20000.0 1000ms  1250ms  

Finally, reducing the padding length to a value less than the data truncates the data instead of generating errors, very handy! Changing the "columnDefaultPadLength" from 8 to 4 results in this output:

n       resuresutimetime
------------------------
5       1000200010001250

Obviously not a desired format, but with the simple adjustment of the padding length, you can quickly tweak the table into a compact yet readable form.