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