loading...

Daily Challenge #19 - Turn numbers into words

twitter logo ・1 min read

Daily Challenge (165 Part Series)

1) Daily Challenge #1 - String Peeler 2) Daily Challenge #2 - String Diamond 3 ... 163 3) Daily Challenge #3 - Vowel Counter 4) Daily Challenge #4 - Checkbook Balancing 5) Daily Challenge #5 - Ten Minute Walk 6) Daily Challenge #6 - Grandma and her friends 7) Daily Challenge #7 - Factorial Decomposition 8) Daily Challenge #8 - Scrabble Word Calculator 9) Daily Challenge #9 - What's Your Number? 10) Daily Challenge #10 - Calculator 11) Daily Challenge #11 - Cubic Numbers 12) Daily Challenge #12 - Next Larger Number 13) Daily Challenge #13 - Twice Linear 14) Daily Challenge #14 - Square into Squares 15) Daily Challenge #15 - Stop gninnipS My sdroW! 16) Daily Challenge #16 - Number of People on the Bus 17) Daily Challenge #17 - Double Trouble 18) Daily Challenge #18 - Triple Trouble 19) Daily Challenge #19 - Turn numbers into words 20) Daily Challenge Post #20 - Number Check 21) Daily Challenge #21 - Human Readable Time 22) Daily Challenge #22 - Simple Pig Latin 23) Daily Challenge #23 - Morse Code Decoder 24) Daily Challenge #24 - Shortest Step 25) Daily Challenge #25 - Double Cola 26) Daily Challenge #26 - Ranking Position 27) Daily Challenge #27 - Unlucky Days 28) Daily Challenge #28 - Kill the Monster! 29) Daily Challenge #29 - Xs and Os 30) Daily Challenge #30 - What is the price? 31) Daily Challenge #31 - Count IPv4 Addresses 32) Daily Challenge #32 - Hide Phone Numbers 33) Daily Challenge #33 - Did you mean...? 34) Daily Challenge #34 - WeIrD StRiNg CaSe 35) Daily Challenge #35 - Find the Outlier 36) Daily Challenge #36 - Let's go for a run! 37) Daily Challenge #37 - Name Swap 38) Daily Challenge #38 - Middle Name 39) Daily Challenge #39 - Virus 40) Daily Challenge #40 - Counting Sheep 41) Daily Challenge #41 - Greed is Good 42) Daily Challenge #42 - Caesar Cipher 43) Daily Challenge #43 - Boardgame Fight Resolver 44) Daily Challenge #44 - Mexican Wave 45) Daily Challenge #45 - Change Machine 46) Daily Challenge #46 - ??? 47) Daily Challenge #47 - Alphabets 48) Daily Challenge #48 - Facebook Likes 49) Daily Challenge #49 - Dollars and Cents 50) Daily Challenge #50 - Number Neighbor 51) Daily Challenge #51 - Valid Curly Braces 52) Daily Challenge #52 - Building a Pyramid 53) Daily Challenge #53 - Faro Shuffle 54) Daily Challenge #54 - What century is it? 55) Daily Challenge #55 - Building a Pile of Cubes 56) Daily Challenge #56 - Coffee Shop 57) Daily Challenge #57 - BMI Calculator 58) Daily Challenge #58 - Smelting Iron Ingots 59) Daily Challenge #59 - Snail Sort 60) Daily Challenge #60 - Find the Missing Letter 61) Daily Challenge #61 - Evolution Rate 62) Daily Challenge #62 - Josephus Survivor 63) Daily Challenge #63- Two Sum 64) Daily Challenge #64- Drying Potatoes 65) Daily Challenge #65- A Disguised Sequence 66) Daily Challenge #66- Friend List 67) Daily Challenge #67- Phone Directory 68) Daily Challenge #68 - Grade Book 69) Daily Challenge #69 - Going to the Cinema 70) Daily Challenge #70 - Pole Vault Competition Results 71) Daily Challenge #71 - See you next Happy Year 72) Daily Challenge #72 - Matrix Shift 73) Daily Challenge #73 - ATM Heist 74) Daily Challenge #74 - Free Pizza 75) Daily Challenge #75 - Set Alarm 76) Daily Challenge #76 - Bingo! (or not...) 77) Daily Challenge #77 - Bird Mountain 78) Daily Challenge #78 - Number of Proper Fractions with Denominator d 79) Daily Challenge #79 - Connect Four 80) Daily Challenge #80 - Longest Vowel Change 81) Daily Challenge #81 - Even or Odd 82) Daily Challenge #82 - English Beggars 83) Daily Challenge #83 - Deodorant Evaporator 84) Daily Challenge #84 - Third Angle of a Triangle 85) Daily Challenge #85 - Unwanted Dollars 86) Daily Challenge #86 - Wouldn't, not Would. 87) Daily Challenge #87 - Pony Express 88) Daily Challenge #88 - Recursive Ninjas 89) Daily Challenge #89 - Extract domain name from URL 90) Daily Challenge #90 - One Step at a Time 91) Daily Challenge #91 - Bananas 92) Daily Challenge #92 - Boggle Board 93) Daily Challenge #93 - Range Extraction 94) Daily Challenge #94 - Last Digit 95) Daily Challenge #95 - CamelCase Method 96) Daily Challenge #96 - Easter Egg Crush Test 97) Daily Challenge #97 - Greed is Good 98) Daily Challenge #98 - Make a Spiral 99) Daily Challenge #99 - Balance the Scales 100) Daily Challenge #100 - Round Up 101) Daily Challenge #101 - Parentheses Generator 102) Daily Challenge #102 - Pentabonacci 103) Daily Challenge #103 - Simple Symbols 104) Daily Challenge #104 - Matrixify 105) Daily Challenge #105 - High-Sum Matrix Drop 106) Daily Challenge #106 - Average Fuel Consumption 107) Daily Challenge #107 - Escape the Mines 108) Daily Challenge #108 - Find the Counterfeit Coin 109) Daily Challenge #109 - Decorate with Wallpaper 110) Daily Challenge #110 - Love VS. Friendship 111) Daily Challenge #111 - 99 Bottles of Beer 112) Daily Challenge #112 - Functions of Integers on the Cartesian Plane 113) Daily Challenge #113 - Iterative Rotation Cipher 114) Daily Challenge #114 - Speed Control 115) Daily Challenge #115 - Look and Say Sequence 116) Daily Challenge #116 - Shortest Knight Path 117) Daily Challenge #117 - MinMinMax 118) Daily Challenge #118 - Reversing a Process 119) Daily Challenge #119 - Adding Big Numbers 120) Daily Challenge #120 - Growth of a Population 121) Daily Challenge #121 - Who has the most money? 122) Daily Challenge #122 - Clockwise Spirals 123) Daily Challenge #123 - Curry me Softly 124) Daily Challenge #124 - Middle Me 125) Daily Challenge #125 - 23 Matches or More 126) Daily Challenge #126 - The Supermarket Line 127) Daily Challenge #127 - Playing with Passphrases 128) Daily Challenge #128 - Blackjack Scorer 129) Daily Challenge #129 - Clay Pigeon Shooting 130) Daily Challenge #130 - GCD Sum 131) Daily Challenge #131 - Remove Anchor from URL 132) Daily Challenge #132 - Is my friend cheating? 133) Daily Challenge #133 - Suitcase Packing 134) Daily Challenge #134 - Rice and Chessboard Problem 135) Daily Challenge #135 - The Wide Mouthed Frog! 136) Daily Challenge #136 - The Deaf Rats of Hamelin 137) Daily Challenge #137 - Help the Bookseller 138) Daily Challenge #138 - Do I get a Bonus? 139) Daily Challenge #138 - Keep Up the Hoop 140) Daily Challenge #140 - I love you, a little, a lot, passionately ... not at all 141) Daily Challenge #141 - Two Sum 142) Daily Challenge #142 - Parts of a Whole 143) Daily Challenge #143 - Big Arithmetic 144) Daily Challenge #144 - Box Office Clerk 145) Daily Challenge #145 - SET Card Game 146) Daily Challenge #146 - The Dollar Game 147) Daily Challenge #147 - NIM 148) Daily Challenge #148 - Disemvowel Trolls 149) Daily Challenge #149 - Fun with Lamps 150) Daily Challenge #150 - Boggle Guess Validator 151) Daily Challenge #151 - Reverse Parentheses 152) Daily Challenge #152 - Strongest Number in an Interval 153) Daily Challenge #153 - Horse Race Gamble 154) Daily Challenge #154 - Stable Arrangement 155) Daily Challenge #155 - Royal Arranged Marriages 156) Daily Challenge #162 - Taxi Dispatching 157) Daily Challenge #163 - Significant Figures 158) Daily Challenge #164 - Jump 159) Daily Challenge #165 - Password Lost in a Grid 160) Daily Challenge #166 - Cat and Mouse 161) Daily Challenge #167 - Return String As Sorted Blocks 162) Daily Challenge #168 - [Code golf] f (f (f b)) = f b 163) Daily Challenge #169 - Christmas Tree 164) Daily Challenge #170 - Pokemon Damage Calculator 165) Daily Challenge #171 - Ordering Beers in Poland

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!

twitter logo DISCUSS (11)
markdown guide
 
 

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"
 

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

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;
 

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()));
    }
}
 

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.

 

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

 

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.

 
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[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(" ");
}

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

 

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

Classic DEV Post from Apr 5 '19

What was your win this week?

Got to all your meetings on time? Started a new project? Fixed a tricky bug?

dev.to staff profile image
The hardworking team behind dev.to ❤️