# Daily Challenge #19 - Turn numbers into words

We have another challenge from MMMAAANNN on Codewars. Today, you are asked to:

Turn a given number (an integer > 0, < 1000) into the equivalent English words.
For example:

wordify(1) == "one"
wordify(12) == "twelve"
wordify(17) == "seventeen"
wordify(56) == "fifty six"
wordify(90) == "ninety"
wordify(326) == "three hundred twenty six"

Good luck, and happy coding!

Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge for a future post? Email yo+challenge@dev.to with your suggestions!

### Discussion Common Lisp is a good choice for this:

``````(defun wordify (n)
(format nil "~r" n))

(wordify 123456789)
;; => "one hundred twenty-three million four hundred fifty-six thousand seven hundred eighty-nine"
``````

JavaScript

``````const letterifyNumber = number => {

const singulars = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"];
const decens = ["", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];
const teens = ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "egighteen", "nineteen"];
const units = ["", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sixtillion", "septillion"];
const blocks = number.toString().split('').reverse().join('').match(/.{1,3}/g);

let numberString = "";
for (let x = 0; x < blocks.length; x++) {
let sectionString = "";
if (parseInt(blocks[x]) !== 0) {
if (blocks[x].length > 2 && blocks[x] !== "0") {
sectionString += `\${singulars[blocks[x]]} hundred `
}
if (blocks[x].length > 1) {
if (blocks[x] === "1") {
sectionString += `\${teens[blocks[x]]} `
} else {
sectionString += `\${decens[blocks[x]]} `;
}
}
if (blocks[x] !== "1") {
sectionString += `\${singulars[blocks[x]]} `
}
sectionString += `\${units[x]} `
numberString = sectionString + numberString;
}
}

return numberString;
}
``````

Live demo on CodePen.

I tried to not dictionary everything, but the exceptions are real. Lots of amusing output as I got closer, that I'll paste for your enjoyment. I did actually get it working though :P

``````#15
fiveteen

# 818
eight hundred eightteen eight

# 101
one hundred oneteen one

# 654
six hundred six hundred six hundred
``````
``````#!/usr/bin/env python

# 0 < num < 1000

num_map = {
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
'5': 'five',
'6': 'six',
'7': 'seven',
'8': 'eight',
'9': 'nine',
'10': 'ten',
'11': 'eleven',
'12': 'twelve',
'13': 'thirteen',
'14': 'fourteen',
'15': 'fifteen',
'18': 'eighteen',
'20': 'twenty',
'30': 'thirty',
'50': 'fifty',
'80': 'eighty'
}

num = input("Enter a number: ").lstrip('0')
inum = int(num)
word = ''

for _ in range(len(num)):
if num in num_map:
word = f'{word} {num_map[num]}'
break
elif inum < 20:
lsi = num
word = f'{word} {num_map[lsi]}teen'
break
elif inum < 100:
tendigit = num
tenword = f'{tendigit}0'
if f'{tenword}' in num_map:
word = f'{word} {num_map[tenword]}'
else:
word = f'{word} {num_map[tendigit]}ty'
elif inum < 1000:
hdigit = num
word = f'{word} {num_map[hdigit]} hundred'
num = num[1:].lstrip('0')
inum = int(num)

print(word.strip())
``````

In Perl, we have CPAN, where you can find modules for almost any task imaginable. Including Lingua::EN::Numbers.

``````#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

use Lingua::EN::Numbers qw{ num2en };

say "\$_: ", num2en(\$_) for 1 .. 999;
``````

Here's a JS one that goes into the gazillions 🤔

Rust Solution!

Pretty hard coded. Probably could be made much more compact and general. But this satisfies the requirements!

``````type Error = &'static str;

fn digit_to_string(s: &str) -> Option<&'static str> {
match s {
"0" => Some(""),
"1" => Some("one"),
"2" => Some("two"),
"3" => Some("three"),
"4" => Some("four"),
"5" => Some("five"),
"6" => Some("six"),
"7" => Some("seven"),
"8" => Some("eight"),
"9" => Some("nine"),
_ => None,
}
}

fn tens_digit_to_string(s: &str) -> Option<&'static str> {
match s {
"2" => Some("twenty"),
"3" => Some("thirty"),
"4" => Some("fourty"),
"5" => Some("fifty"),
"6" => Some("sixty"),
"7" => Some("seventy"),
"8" => Some("eighty"),
"9" => Some("ninety"),
_ => None,
}
}

fn double_digit_to_string(s: &str) -> Option<String> {
if &s[0..1] == "1" {
Some(
match s {
"10" => "ten",
"11" => "eleven",
"12" => "twelve",
"13" => "thirteen",
"14" => "fourteen",
"15" => "fifteen",
"16" => "sixteen",
"17" => "seventeen",
"18" => "eighteen",
"19" => "nineteen",
_ => unreachable!("Can't get here"),
}
.to_string(),
)
} else {
Some(
format!(
"{} {}",
tens_digit_to_string(&s[0..1]).unwrap(),
digit_to_string(&s[1..2]).unwrap()
)
.trim()
.to_string(),
)
}
}

pub fn wordify(num: u32) -> Result<String, Error> {
if num == 0 || num >= 1000 {
return Err("Invalid Number");
}

let num_string = num.to_string();

match num_string.len() {
1 => Ok(digit_to_string(&num_string).unwrap().to_string()),
2 => Ok(double_digit_to_string(&num_string).unwrap()),
3 => Ok(format!(
"{} hundred {}",
digit_to_string(&num_string[0..1]).unwrap(),
double_digit_to_string(&num_string[1..3]).unwrap()
)
.trim()
.to_string()),
_ => unreachable!("Already check for numbers larger"),
}
}

#[cfg(test)]
mod tests {
use crate::wordify;

#[test]
fn it_return_an_error_for_invalid_numbers() {
assert_eq!(wordify(0), Err("Invalid Number"));
assert_eq!(wordify(1000), Err("Invalid Number"));
assert_eq!(wordify(1111), Err("Invalid Number"));
}

#[test]
fn it_works_for_the_examples() {
assert_eq!(wordify(1), Ok("one".to_string()));
assert_eq!(wordify(12), Ok("twelve".to_string()));
assert_eq!(wordify(17), Ok("seventeen".to_string()));
assert_eq!(wordify(56), Ok("fifty six".to_string()));
assert_eq!(wordify(90), Ok("ninety".to_string()));
assert_eq!(wordify(326), Ok("three hundred twenty six".to_string()));
}
}
``````

``````const ones = {
1: "One",
2: "Two",
3: "Three",
4: "Four",
5: "Five",
6: "Six",
7: "Seven",
8: "Eight",
9: "Nine"
};

const tens = {
10: "Ten",
11: "Eleven",
12: "Tweleve",
13: "Thirteen",
14: "Fourteen",
15: "Fifteen",
16: "Sixteen",
17: "Seventeen",
18: "Eighteen",
19: "Nineteen"
};

const places = {
2: "Twenty",
3: "Thirty",
4: "Fourty",
5: "Fifty",
6: "Sixty",
7: "Seventy",
8: "Eighty",
9: "Ninety"
};

const place_map = index => places[index] || "";

let place_func = number => {
if (number > 0 && number < 10) {
return ones[number];
} else if (number >= 10 && number <= 19) {
return tens[number];
} else if (number > 19 && number <= 99) {
return `\${place_map(parseInt(number / 10))} \${place_func(number % 10)}`;
} else if (number > 99 && number < 1000) {
return `\${ones[parseInt(number / 100)]} Hundred \${place_func(
number % 100
)}`;
}
return "";
};

console.log(place_func(101));
``````

English is fun what with all the exceptions...

``````function numToText(n) {
if (n < 1 || n > 999 || n === undefined) {
throw new Error(`arg must be > 0 and < 1000`);
}
const ones = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"];
const prefixes = ["thir", "four", "fif", "six", "seven", "eigh", "nine"];
const tys = ["", "", "twenty", ...prefixes.map(prefix => `\${prefix}ty`)];
tys = "forty";
const oneTeens = [...ones, "ten", "eleven", "twelve", ...prefixes.map(prefix => `\${prefix}teen`)];

const parts = [];
let rem = n;
if (rem >= 100) {
parts.push(ones[Math.floor(rem / 100)], "hundred")
rem = rem % 100;
}
if (rem >= 20) {
parts.push(tys[Math.floor(rem / 10)]);
rem = rem % 10;
}
if (rem > 0 && rem < 20) {
parts.push(oneTeens[rem]);
}
return parts.join(" ");
}
``````

Try French. I’ll pass you the details of “quatre-vingt-dix-neuf” for 99, but you also have “vingt et un” (21), “vingt deux” (22), “deux cents” (200, note the s) but “deux cent un” (201).

I’m learning Erlang, and I got to use file I/O and a list comprehension for the unit tests (in the Gist version).

Short version:

``````-module( words ).
-export( [ num_to_words/1, test_words/2 ] ).

-include_lib("eunit/include/eunit.hrl").

words() -> #{
units => [
"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
],
tens => [ "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" ]
}.
word( N, Cat ) ->
lists:nth( N, maps:get( Cat, words() ) ).

join_num_words( _, 0 ) ->
"";
join_num_words( Joint, N ) ->
Joint ++ num_to_words( N ).

num_to_words( N ) when N > 0, N < 20 ->
word( N, units );
num_to_words( N ) when N < 100 ->
word( N div 10, tens ) ++ join_num_words( "-", N rem 10 );
num_to_words( N ) when N < 1000 ->
word( N div 100, units ) ++ " hundred" ++ join_num_words( " and ", N rem 100 ).

% TESTS

num_to_words_test_() -> [
?_assertEqual( "one", num_to_words( 1 ) ),
?_assertEqual( "four", num_to_words( 4 ) ),
?_assertEqual( "ten", num_to_words( 10 ) ),
?_assertEqual( "twelve", num_to_words( 12 ) ),
?_assertEqual( "twenty", num_to_words( 20 ) ),
?_assertEqual( "forty-two", num_to_words( 42 ) ),
?_assertEqual( "one hundred", num_to_words( 100 ) ),
?_assertEqual( "one hundred and one", num_to_words( 101 ) ),
?_assertEqual( "one hundred and fifty-six", num_to_words( 156 ) ),
?_assertEqual( "eight hundred and sixty-five", num_to_words( 865 ) ),
?_assertEqual( "nine hundred and ninety-nine", num_to_words( 999 ) )
].
``````

In this Gist, there’s a test file with all values from 1 to 999, and a EUnit test that loads this file and generates a test for each value.  