DEV Community

dev.to staff
dev.to staff

Posted on

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!

Top comments (11)

Collapse
 
martin_cerdeira profile image
Super Intergalactic Tinchi 🐠 • Edited

I would cheat and use google translate api xD

Drag Racing

Collapse
 
alvaromontoro profile image
Alvaro Montoro

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][2] !== "0") { sectionString += `${singulars[blocks[x][2]]} hundred ` } if (blocks[x].length > 1) { if (blocks[x][1] === "1") { sectionString += `${teens[blocks[x][0]]} ` } else { sectionString += `${decens[blocks[x][1]]} `; } } if (blocks[x][1] !== "1") { sectionString += `${singulars[blocks[x][0]]} ` } sectionString += `${units[x]} ` numberString = sectionString + numberString; } } return numberString; } 

Live demo on CodePen.

Collapse
 
gypsydave5 profile image
David Wickes • Edited

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" 
Collapse
 
_morgan_adams_ profile image
morgana • Edited

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[1] word = f'{word} {num_map[lsi]}teen' break elif inum < 100: tendigit = num[0] 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[0] word = f'{word} {num_map[hdigit]} hundred' num = num[1:].lstrip('0') inum = int(num) print(word.strip()) 
Collapse
 
choroba profile image
E. Choroba

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; 
Collapse
 
mygtma profile image
Avi
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)); 
Collapse
 
coreyja profile image
Corey Alexander • Edited

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())); } } 
Collapse
 
willsmart profile image
willsmart • Edited

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

Collapse
 
oscherler profile image
Olivier “Ölbaum” Scherler • Edited

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.

Collapse
 
kerrishotts profile image
Kerri Shotts

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[4] = "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(" "); } 
Enter fullscreen mode Exit fullscreen mode

Gist: gist.github.com/kerrishotts/ea2bd9...

Collapse
 
oscherler profile image
Olivier “Ölbaum” Scherler

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