ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# SergiiVlasiuk's solution

## to Say in the Scala Track

Published at Sep 09 2019 · 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".

The Scala exercises assume an SBT project scheme. The exercise solution source should be placed within the exercise directory/src/main/scala. The exercise unit tests can be found within the exercise directory/src/test/scala.

To run the tests simply run the command `sbt test` in the exercise directory.

For more detailed info about the Scala track see the help page.

## 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.

### SayTest.scala

``````import org.scalatest.{Matchers, FunSuite}

/** @version 1.1.0 */
class SayTest extends FunSuite with Matchers {

test("zero") {
Say.inEnglish(0) should be(Some("zero"))
}

test("one") {
pending
Say.inEnglish(1) should be(Some("one"))
}

test("fourteen") {
pending
Say.inEnglish(14) should be(Some("fourteen"))
}

test("twenty") {
pending
Say.inEnglish(20) should be(Some("twenty"))
}

test("twenty-two") {
pending
Say.inEnglish(22) should be(Some("twenty-two"))
}

test("one hundred") {
pending
Say.inEnglish(100) should be(Some("one hundred"))
}

test("one hundred twenty-three") {
pending
Say.inEnglish(123) should be(Some("one hundred twenty-three"))
}

test("one thousand") {
pending
Say.inEnglish(1000) should be(Some("one thousand"))
}

test("one thousand two hundred thirty-four") {
pending
Say.inEnglish(1234) should be(Some("one thousand two hundred thirty-four"))
}

test("one million") {
pending
Say.inEnglish(1000000) should be(Some("one million"))
}

test("one million two thousand three hundred forty-five") {
pending
Say.inEnglish(1002345) should be(Some("one million two thousand three hundred forty-five"))
}

test("one billion") {
pending
Say.inEnglish(1000000000) should be(Some("one billion"))
}

test("a big number") {
pending
Say.inEnglish(987654321123l) should be(Some("nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three"))
}

test("numbers below zero are out of range") {
pending
Say.inEnglish(-1) should be(None)
}

test("numbers above 999,999,999,999 are out of range") {
pending
Say.inEnglish(1000000000000l) should be(None)
}
}``````
``````import scala.collection.immutable.TreeMap

object Say {
type NumberLookupType = (Long, Long, Long, String)
type NumberOfSp = (Long, Long, String)
val MaxVal = 999999999999l
private val vocabulary: Map[Long, String] = TreeMap(
1l -> "one",
2l -> "two",
3l -> "three",
4l -> "four",
5l -> "five",
6l -> "six",
7l -> "seven",
8l -> "eight",
9l -> "nine",
10l -> "ten",
11l -> "eleven",
12l -> "twelve",
13l -> "thirteen",
14l -> "fourteen",
15l -> "fifteen",
16l -> "sixteen",
17l -> "seventeen",
18l -> "eighteen",
19l -> "nineteen",
20l -> "twenty",
30l -> "thirty",
40l -> "forty",
50l -> "fifty",
60l -> "sixty",
70l -> "seventy",
80l -> "eighty",
90l -> "ninety",
100l -> "hundred",
1000l -> "thousand",
1E6.toLong -> "million",
1E9.toLong -> "billion"
)(Ordering[Long].reverse)

def translate(num: Long): String = {
def findNum(curNum: Long): Option[NumberLookupType] =
vocabulary.find { case (k, _) => k <= curNum } match {
case Some((k, v)) => Some(curNum / k, curNum % k, k, v)
case _ => None
}

def loop(curNum: Long, acc: List[NumberOfSp]): List[NumberOfSp] = {
findNum(curNum) match {
case None => acc.reverse
case Some((numItems, rem, value, spelling)) => {
val newList = (numItems, value, spelling) :: acc
if (rem == 0) newList.reverse
else if (value < 100) ((1l, value, spelling + "-" + vocabulary.getOrElse(rem, "")) :: acc).reverse
else loop(rem, newList)
}
}
}

loop(num, List()).map { case (num, value, sp) =>
if (value < 100) sp
else translate(num) + " " + sp
}.mkString(" ")
}

def inEnglish(num: Long): Option[String] = {
if (num < 0 || num > MaxVal) None
else if (num == 0) Some("zero")
else Some(translate(num))
}
}``````