5

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?

Jack Hayton
  • 363
  • 4
  • 18
  • 1
    try this one: http://stackoverflow.com/questions/28138689/format-println-output-in-a-table – Apple-and-Oranges Jul 24 '15 at 13:16
  • @Apple-and-Oranges, Thanks for linking that question, that provides me with a way to find the max length of the data, however it says "Then pad those strings". I am new to programming and I don't know what that means, nor how to do it. Do you know how? – Jack Hayton Jul 24 '15 at 13:41
  • Can you show the declaration of rainfall, raindays etc.? Are they array of Ints, or array of Strings? – Mario Zannone Jul 24 '15 at 13:47
  • @MarioZannone, I have edited the question to include the declarations exactly as I have them in the program. I believe that months is of type String and the remaining arrays are of type Int. Hope this helps – Jack Hayton Jul 24 '15 at 13:55

5 Answers5

6

Try this:

for i in 0...11 {
  let month = (months[i] as NSString).UTF8String
  println(String(format:"%-10s %10d %10d %10d",  month, 
                 rainfall[i], raindays[i], water_deficit[i]))
}

For details un format and format specifiers see here and here.

In let month = (months[i] as NSString).UTF8String I am applying a conversion from String to C String: it is pretty easy to specify a length in the format specifier of a C String, but I don't know how to do it for a String (my guess is that it is not possible).

Mario Zannone
  • 2,843
  • 12
  • 18
  • 1
    Thank you for this, this works perfectly! However I do not understand it, could you please explain what .UTF8String is and what the %-10s and the %10d do. If this may seem obvious, I'm very new to this so I haven't seen this before. Thanks! – Jack Hayton Jul 24 '15 at 14:39
  • Yes. I just added a couple of explanations and references. – Mario Zannone Jul 24 '15 at 15:06
2

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

Shripada
  • 6,296
  • 1
  • 30
  • 30
  • Thank you for this post, I tried to copy and paste this into a new playground but there are several errors in the code. The first error is in the command "variablename.utf8.count" is showing the error: String.UTF8View does not have a member named count (occurs twice). The second error is the first print statement in the for loop. The error is: "Cannot find an overload for 'print' that accept..." I am new to programming so I do not know what these errors mean or how to fix them. – Jack Hayton Jul 24 '15 at 14:34
  • Oh,forgot to mention, this is Swift 2.0, Xcode 7 beta3. – Shripada Jul 25 '15 at 04:26
1
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")

}

Sandeep Jangir
  • 412
  • 5
  • 14
0

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

Sandeep Jangir
  • 412
  • 5
  • 14
  • 1
    I know that this is the problem, I just don't know how to fix it! Changing the names of the months is possible, but I feel thats not solving the problem, its simply covering it up. Additionally, I would like to know how to do this in a situation where the values printed in the table change (depending on user input), for instance in one case the number may read 101 (3 characters). If the user were to input a different value in the begging of the program that same number may read 1000 (4 characters) and so the same problem would occur. There must be a method to create a column of a given width. – Jack Hayton Jul 24 '15 at 13:59
  • 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") } – Sandeep Jangir Jul 24 '15 at 14:10
  • just copy paste this code @JackHayton... it will help you in all possible case :) – Sandeep Jangir Jul 24 '15 at 14:11
  • I have copied the code but it did not work, initially the program wanted me to add a semisolon (;) between consecutive commands, even once that had been done it did not work. I would prefer if you knew a method to the solution that I could understand and apply in any situation. – Jack Hayton Jul 24 '15 at 14:18
  • i am uploading another answer for better viewing :) – Sandeep Jangir Jul 24 '15 at 17:38
0
// 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.

Zezeron
  • 57
  • 1
  • 4