'Generating a table with columns of equal width (viewed on the console in Xcode)

This is a piece of code that I am using to check my calculations, I am simply writing out these values into the console in Xcode. Each of the arrays is declared with the values that are shown below.

var water_deficit: [Int] = []

The program calculates values for water deficit and appends them into this list (the calculations are not shown)

let months = ["January","Feburary","March","April","May","June","July","August","September","October","November","December"]
let rainfall = [38,94,142,149,236,305,202,82,139,222,178,103]
let raindays = [3,6,8,7,12,16,10,8,12,14,11,7]
for i in 0...11 {
    println("\(months[i]) \t \(rainfall[i]) \t \(raindays[i]) \t \(water_deficit[i])")
}

The output as shown on the console:

Month    Rainfall    Raindays    Water Deficit
January      38      3   38
Feburary     94      6   -18
March    142     8   -8
April    149     7   -1
May      236     12      116
June     305     16      301
July     202     10      202
August   82      8   82
September    139     12      101
October      222     14      203
November     178     11      208
December     103     7   103

As you can see, because the length of the words/numbers are different, the columns are offset. What do I need to do to generate columns of a specific width to avoid this problem?



Solution 1:[1]

The trick is to compute right paddings to the left or to the right of a string unit depending on if you want the string to be right or left justified. Here is a quick implementation. (Swift 2.0, Xcode 7 beta3).

  let headings = ["Month    ", "Rainfall", "RainDays", "Water Deficit"]

let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]

let rainFalls = [38, 94, 142,149,236,305,202, 82, 139, 222, 178, 103]
let rainyDays = [3, 6, 8,7,12,16,10, 8, 12, 14, 11, 7]
let waterDeficits = [38, -18, -8,-1,116,301,202, 82, 101, 203, 208, 103]


func getRightJustifiedStringRepFor(number: Int, refString:String) -> String
{
    let length = refString.utf8.count
    let stringRep = String(number)

    var paddedStringRep : String = ""

    //Build necessary padding
    for  var i = 0 ; i <  (length - stringRep.utf8.count) ; i++
    {
        paddedStringRep += " "
    }

    paddedStringRep += stringRep

    return paddedStringRep
}


let headingsToDisplay = headings.reduce(""){

    (var accummulated : String, item: String) -> String in
    return accummulated  + item +  "\t\t\t"

}

print(headingsToDisplay)

//Get proper aligned months with forward padding as we want them left aligned
let leftJustifiedMonths = months.map{
    (var item: String) -> String in
    let paddingsNeeded = 9 - item.utf8.count  //9 is the  length of lengthy month name
    for var i = 0 ; i < paddingsNeeded ; i++
    {
        item += " "
    }
    return item
}

print("\n")

for i in 0...11
{
    print(leftJustifiedMonths[i], appendNewline:false)
    print("\t\t\t", appendNewline:false)
    print( (getRightJustifiedStringRepFor(rainFalls[i], refString: "Rainfall")), appendNewline:false)
    print("\t\t\t", appendNewline:false)
    print( (getRightJustifiedStringRepFor(rainyDays[i], refString: "RainDays")),appendNewline:false)
    print("\t\t\t", appendNewline:false)
    print( (getRightJustifiedStringRepFor(waterDeficits[i], refString: "Water Deficit")),appendNewline:false)

    print("\n")

}

This outputs:

enter image description here

Solution 2:[2]

for i in 0...11 {
print(months[i])
countElements(months[i]) > 4 ? print("\t\t") : print("\t\t\t")

print(rainfall[i])
countElements(String(rainfall[i])) > 4 ? print("\t\t") : print("\t\t\t")

print(raindays[i])
countElements(String(raindays[i])) > 4 ? print("\t\t") : print("\t\t\t")

print(water_deficit[i])
print("\n")

}

Solution 3:[3]

This is happing because your string length and your variable length are mismatching and \t that cause tab in line tab from the string length.

So there are two ways to stop this :

  1. Simplest way -> just make all months name in a same length this will remove the mismatch very easily

    let months : [String] = ["jan","feb","mar","apr","may","june","july","aug","sept","oct","nov","dec"]

  2. make your variable in such a format like this:

    let nf = NSNumberFormatter() nf.numberStyle = NSNumberFormatterStyle.DecimalStyle

Solution 4:[4]

// This basically months max string count calculation and how many spaces needed to print
        var maxMountStringCount = 0
        let months = ["January", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
        months.forEach({ (month) in
            if month.count > maxMountStringCount {
                maxMountStringCount = month.count
            }
        })
        months.forEach({ (month) in
            let keySpace = String(repeating: " ", count: maxMountStringCount - month.count)
            print("\t?\t\(month)\(keySpace) :\t\(month) ")
        })

Console Preview is;

?   January   : January 
?   Feburary  : Feburary 
?   March     : March 
?   April     : April 
?   May       : May 
?   June      : June 
?   July      : July 
?   August    : August 
?   September : September 
?   October   : October 
?   November  : November 
?   December  : December 

It's simple. You can Develop it up to your needs.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Sandeep Jangir
Solution 3 Sandeep Jangir
Solution 4 Zezeron