mgeilich's solution

to Say in the Swift Track

Published at Jul 13 2018 · 0 comments
Instructions
Test suite
Solution

Given a number from 0 to 999,999,999,999, spell out that number in English.

Step 1

Handle the basic case of 0 through 99.

If the input to the program is `22`, then the output should be `'twenty-two'`.

Your program should complain loudly if given a number outside the blessed range.

Some good test cases for this program are:

• 0
• 14
• 50
• 98
• -1
• 100

Extension

If you're on a Mac, shell out to Mac OS X's `say` program to talk out loud.

Step 2

Implement breaking a number up into chunks of thousands.

So `1234567890` should yield a list like 1, 234, 567, and 890, while the far simpler `1000` should yield just 1 and 0.

The program must also report any values that are out of range.

Step 3

Now handle inserting the appropriate scale word between those chunks.

So `1234567890` should yield `'1 billion 234 million 567 thousand 890'`

The program must also report any values that are out of range. It's fine to stop at "trillion".

Step 4

Put it all together to get nothing but plain English.

`12345` should give `twelve thousand three hundred forty-five`.

The program must also report any values that are out of range.

Extensions

Use and (correctly) when spelling out the number in English:

• 14 becomes "fourteen".
• 100 becomes "one hundred".
• 120 becomes "one hundred and twenty".
• 1002 becomes "one thousand and two".
• 1323 becomes "one thousand three hundred and twenty-three".

Setup

Go through the project setup instructions for Xcode using Swift:

http://exercism.io/languages/swift

Source

A variation on JavaRanch CattleDrive, exercise 4a http://www.javaranch.com/say.jsp

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

LinuxMain.swift

``````import XCTest
@testable import SayTests

XCTMain([
testCase(SayTests.allTests),
])``````

SayTests.swift

``````import XCTest
@testable import Say

class SayTests: XCTestCase {

func testZero() {
XCTAssertEqual("zero", Say.say(0))
}

func testOne() {
XCTAssertEqual("one", Say.say(1))
}

func testFourteen() {
XCTAssertEqual("fourteen", Say.say(14))
}

func testTwenty() {
XCTAssertEqual("twenty", Say.say(20))
}

func testTwentyTwo() {
XCTAssertEqual("twenty-two", Say.say(22))
}

func testOneHundred() {
XCTAssertEqual("one hundred", Say.say(100))
}

func testOneHundredTwentyThree() {
XCTAssertEqual("one hundred twenty-three", Say.say(123))
}

func testOneThousand() {
XCTAssertEqual("one thousand", Say.say(1_000))
}

func testOneThousandTwoHundredThirtyFour() {
XCTAssertEqual("one thousand two hundred thirty-four", Say.say(1_234))
}

func testOneMillion() {
XCTAssertEqual("one million", Say.say(1_000_000))
}

func testOneMillionTwoThousandThreeHundredFortyFive() {
XCTAssertEqual("one million two thousand three hundred forty-five", Say.say(1_002_345))
}

func testOneBillion() {
XCTAssertEqual("one billion", Say.say(1_000_000_000))
}

func testABigNumber() {
XCTAssertEqual("nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three", Say.say(987_654_321_123))
}

func testNumbersBelowZeroAreOutOfRange() {
XCTAssertNil(Say.say(-1))
}

func testNumbersAbove999999999999AreOutOfRange() {
XCTAssertNil(Say.say(1_000_000_000_000))
}

static var allTests: [(String, (SayTests) -> () throws -> Void)] {
return [
("testZero", testZero),
("testOne", testOne),
("testFourteen", testFourteen),
("testTwenty", testTwenty),
("testTwentyTwo", testTwentyTwo),
("testOneHundred", testOneHundred),
("testOneHundredTwentyThree", testOneHundredTwentyThree),
("testOneThousand", testOneThousand),
("testOneThousandTwoHundredThirtyFour", testOneThousandTwoHundredThirtyFour),
("testOneMillion", testOneMillion),
("testOneMillionTwoThousandThreeHundredFortyFive", testOneMillionTwoThousandThreeHundredFortyFive),
("testOneBillion", testOneBillion),
("testABigNumber", testABigNumber),
("testNumbersBelowZeroAreOutOfRange", testNumbersBelowZeroAreOutOfRange),
("testNumbersAbove999999999999AreOutOfRange", testNumbersAbove999999999999AreOutOfRange),
]
}
}``````
``````//Solution goes in Sources
import Foundation

func say(_ number: Int) -> String? {
var output = String()

guard number >= 0 && number < 999999999999 else { return nil }
if number < 1000 { return hundred(number) }

var temp = number
var triplets = [Int]()
let buckets = ["", "thousand", "million", "billion", "trillion"]
while temp > 0 {
triplets.append(temp % 1000)
temp /= 1000
}
for i in 1..<triplets.count {
if triplets[i] > 0 {
output = hundred(triplets[i]) + " " + buckets[i] + " " + output
}
}
output += triplets.first! > 0 ? hundred(triplets.first!) + " " : ""
output = String(output.dropLast())

return output
}

func ordinal(_ number: Int) -> String {
let ordinals = [0:"zero", 1:"one", 2:"two", 3:"three", 4:"four", 5:"five", 6:"six", 7:"seven",
8:"eight", 9:"nine", 10:"ten", 11:"eleven", 12:"twelve"]

if let _ = ordinals[number] { return ordinals[number]! }
return ordinals[number % 10]! + "teen"
}

func ten(_ number: Int) -> String {
var output = String()
let tens = [20:"twenty", 30:"thirty", 40:"forty", 50:"fifty",
60:"sixty", 70:"seventy", 80:"eighty", 90:"ninety"]

let div = number / 10 * 10
let rem = number % 10
if let _ = tens[div] {
output = tens[div]!
if rem != 0 { output += "-" + ordinal(rem)}
}
return output
}

func hundred(_ number: Int) -> String {
if number < 20 { return ordinal(number) }
if number < 100 { return ten(number) }

var output = String()
output =  ordinal(number/100) + " hundred"
if number % 100 > 0 {
output += " " + ten(number % 100)
}
return output
}``````