17
\$\begingroup\$

It's 2050, and people have decided to write numbers in a new way. They want less to memorize, and number to be able to be written quicker.
For every place value(ones, tens, hundreds, etc.) the number is written with the number in that place, a hyphen, and the place value name. "zero" and it's place value does not need to be written.
The number 0 and negative numbers do not need to be handled, so don't worry about those.

Input:

The input will be a positive integer up to 3 digits.

Output:

The output should be a string that looks like something below.

Test cases:

56 => five-ten six
11 => ten one
72 => seven-ten two
478 => four-hundred seven-ten eight
754 => seven-hundred five-ten four
750 => seven-hundred five-ten
507 => five-hundred seven

This question would not be a dupe because that one has a bit more requirements.
"One" does not need to be printed unless it is in the ones place value.
This is , so normal golfing rules apply.
Shortest answer wins!
Good luck!

Imagine if numbers are actually written like this in the future...
\$\endgroup\$
12
  • 1
    \$\begingroup\$ Can 'one' be printed, or are we not allowed? \$\endgroup\$
    – jezza_99
    Mar 31, 2022 at 2:31
  • 2
    \$\begingroup\$ This is exactly how numbers in a language some of us in chat designed (katlani) work haha \$\endgroup\$ Mar 31, 2022 at 2:34
  • 13
    \$\begingroup\$ Most east Asian languages influenced by the Chinese numerical system read numbers exactly this way. \$\endgroup\$
    – xiver77
    Mar 31, 2022 at 8:58
  • 2
    \$\begingroup\$ As a Vietnamese, this is how you read numbers. \$\endgroup\$
    – badatgolf
    Mar 31, 2022 at 14:36
  • 2
    \$\begingroup\$ Suggested test case: 110 (which I think should be hundred ten). \$\endgroup\$
    – Arnauld
    Mar 31, 2022 at 21:26

17 Answers 17

10
\$\begingroup\$

Perl 5 -p, 100 bytes

$a="{,{one,two,three,four,five,six,seven,eight,nine}";$_=(<"$a-hundred }$a-ten }$a}">)[$_];s/one-//g

Try it online!

Readable version:

#!perl -pl
$a="{,{one,two,three,four,five,six,seven,eight,nine}";
$_=(<"$a-hundred }$a-ten }$a}">)[$_];
s/one-//g

The idea here is simple: instead of trying to generate the number on the fly, generate a list of all 2050 numbers using a glob expression (which is much easier) and then index into that list. If $n is the list of numbers, we construct the glob {,{$n}-hundred }{,{$n}-ten }{,{$n}}. The only problem is that this glob expression will generate one-ten and one-hundred, which we can simply strip at the end with s/one-//g.

\$\endgroup\$
6
\$\begingroup\$

Vyxal S, 25 bytes

ẏṘ↵Z'h;ƛ∆ċ1∆ċ-';\-jȧ1∆ċ$∨

Try it Online!

or try a test suite.

Thank goodness for built-in number to word support

Explained

ẏṘ↵Z'h;ƛ∆ċ1∆ċ-';\-jȧ1∆ċ$∨ # example input → 701
ẏṘ                        # range(len(input) - 1, -1, -1) → [2, 1, 0]
  ↵                       # 10 to the power of each item in that range → [100, 10, 1]
   Z                      # zip that with the digits of the input → [[7, 100], [0, 10], [1, 1]]
    'h;                   # keep only items where the digit of the input isn't 0 → [[7, 100], [1, 1]]
       ƛ                  # to each item in the remaining [digit, power10] list:
        ∆ċ                #   convert both digit and power10 to words → [["seven", "one hundred"], ["one", "one"]]
          1∆ċ-            #   remove any occurances of the word "one" from each → [["seven", " hundred"], ["", ""]]
              ';          #   and remove any empty strings - this makes it so that you have singleton lists for hundreds and tens and an empty list for ones if one is present → [["seven", " hundred"], []]
                \-j       #   join the result of that on "-" → ["seven- hundred", ""]
                   ȧ      #   remove whitespace → ["seven-hundred", ""]
                    1∆ċ$∨ #   get the first truthy item from that and "one" - this works because an empty string means that we want the word "one" → ["seven-hundred", "one"]
                          # The -S joins the result on spaces → "seven-hundred one"
\$\endgroup\$
5
\$\begingroup\$

Python + num2words, 162 138 137 bytes

lambda N:' '.join([((num2words(n)+'-'*(w>''),'')[n=='1'<w]+w)for n,w in zip('%3s'%N,['hundred','ten',''])if'0'<n])
from num2words import*

Attempt This Online!

Requires the package num2words to be installed, so won't run in the ATO link.

-24 bytes thans to Jonathan Allan

-1 byte, replaced '%03s'%N with '%3s'%N

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Saved 21 bytes and fixed the fact that you have extra spaces when zeros appear: lambda N:' '.join([((num2words(n)+'-'*(w>''),'')[n=='1'>''<w]+w)for n,w in zip('%03s'%N,['hundred','ten',''])if'0'<n]) \$\endgroup\$ Mar 31, 2022 at 12:15
  • 1
    \$\begingroup\$ ...actually since 'hundred' and 'ten' are both greater than '1' while '' is not the n=='1'>''<w above can just be n=='1'<w saving three more bytes. \$\endgroup\$ Mar 31, 2022 at 12:19
4
\$\begingroup\$

Charcoal, 74 bytes

⪫Φ⮌E⮌S⪫Φ⟦§⪪”↶⌈↶?#λ<∕CSG∨⮌№¤"~^hφ﹪▶U⊕hU⦄w↷” ∧∨⊖ι¬κIι§⪪”↶↧WaKπ⁵1✂” ∧Iικ⟧λ-ι 

Try it online! Link is to verbose version of code. Explanation:

     S                                      Input as a string
    ⮌                                       Reversed
   E                                        Map over digits
        ⟦                            ⟧      Tuple of
           ”...”                            Compressed digits string
          ⪪                                 Split on spaces
         §                                  Indexed by
                    ι                       Current digit
                   ⊖                        Decremented
                  ∨                         Logical Or
                      κ                     Current power of 10
                     ¬                      Is zero
                 ∧                          Logical And
                        ι                   Current digit
                       I                    Cast to integer
                           ”...”            Compressed places string
                          ⪪                 Split on spaces
                         §                  Indexed by
                                   ι        Current digit
                                  I         Cast to integer
                                 ∧          Logical And
                                    κ       Current power of 10
       Φ                                    Filtered where
                                      λ     String is not empty
      ⪫                                     Joined with
                                       -    Literal string `-`
  ⮌                                         Reversed
 Φ                                          Filtered where
                                        ι   String is not empty
⪫                                           Joined with spaces
                                            Implicitly print
\$\endgroup\$
4
\$\begingroup\$

C (gcc), 193 171 169 bytes

  • -2 thanks to ceilingcat

Recursively divides the input number, looking up each digit (except for 1 in non-ones position and 0) and and appending the suffix (except for ones position.)

g(v,s,i){v&&g(v/10,s?"hundred":"ten"),(i=v%10)&&printf("%3$.5s%2$s%s "+(i<2&&s)*10,s?:"",!s+"-","one\0 two\0 threefour\0five\0six\0 seveneightnine"+~-i*5);}f(v){g(v,0);}

Try it online!

\$\endgroup\$
0
2
\$\begingroup\$

Factor + math.text.english math.text.utils, 138 136 126 bytes

[ 1 digit-groups [ 10^ [ number>text ] bi@ "-"glue R/ one[- ]|-one$|zero.*/ ""re-replace ] map-index reverse harvest " "join ]

Try it online!

Explanation

Code                     | Data stack                              | Comment
============================================================================================================
                         ! 478
1 digit-groups           ! { 8 7 4 }
           <<inside the map-index quotation now; first iteration>>
                         ! 8 0                                       (digit, index)
10^                      ! 8 1
[ number>text ] bi@      ! "eight" "one"
"-"glue                  ! "eight-one"
R/ one[- ]|-one$|zero.*/ ! "eight-one" R/ one[- ]|-one$|zero.*/      (push a regexp literal on the data stack)
""re-replace             ! "eight"
          <<outside the map-index quotation now>>
map-index                ! { "eight" "seven-ten" "four-hundred" }
reverse                  ! { "four-hundred" "seven-ten" "eight" }
harvest                  ! { "four-hundred" "seven-ten" "eight" }    (remove empty strings)
" "join                  ! "four-hundred seven-ten eight"
\$\endgroup\$
2
\$\begingroup\$

JavaScript (V8), 162 153 151 148 bytes

-3 bytes thanks to Arnauld.
-6 bytes by merging the handling of 10's and 100's.
-2 bytes thanks to Arnauld, again.
-3 bytes by moving the floor operation, thanks to Shaggy.

f=(n,p)=>n>9?f(n/(m=n>99?100:10)|0,m%4?'ten ':'hundred ')+f(n%m):n?--n|!p?'one,two,three,four,five,six,seven,eight,nine'.split`,`[n]+[p&&'-'+p]:p:''

Try it online!

Ungolfed

f = (n, p) =>
  n > 9 ? // check if we should handle 10's or 100's
    f(
      n / (m = n > 99 ? 100 : 10) | 0, // check what mod to use, then floor divide
      m % 4 ? 'ten ' : 'hundred ' // check what place to use
    ) + f(n % m) :
  n ? // if 9 >= n > 0
    --n | !p ? // if n > 1 or p ('ten' or 'hundred') is not defined we need to find the name of n
      'one,two,three,four,five,six,seven,eight,nine'.split`,`[n] +
      [p && '-' + p] : // add p if we have it
    p : // return p if n = 1
  '' // return '' if n = 0
\$\endgroup\$
9
  • 1
    \$\begingroup\$ You can use [p&&'-'+p] instead of (p?'-'+p:''). \$\endgroup\$
    – Arnauld
    Mar 31, 2022 at 8:36
  • 1
    \$\begingroup\$ You can use --n instead of n-1, which allows to get rid of the leading comma in the lookup string. \$\endgroup\$
    – Arnauld
    Mar 31, 2022 at 8:38
  • \$\begingroup\$ @Arnauld Thank you! That's clever to use [] to remove undefined. There's probably a way to avoid duplicate n>99 but I don't see it. \$\endgroup\$ Mar 31, 2022 at 19:47
  • 1
    \$\begingroup\$ You can do m%4?'ten ':'hundred '. \$\endgroup\$
    – Arnauld
    Mar 31, 2022 at 19:56
  • 1
    \$\begingroup\$ You can save another byte with (n|=0) instead of (n=~~n). \$\endgroup\$
    – Arnauld
    Mar 31, 2022 at 20:56
2
\$\begingroup\$

Python, 198 188 bytes

lambda m:" ".join([r[int(i)]for i,r in zip('%3s'%m,[[c+s for c in",one,two,three,four,five,six,seven,eight,nine".split(",")]for s in['-hundred','-ten','']])if"0"<i>" "]).replace("one-","")

Attempt This Online!

Inspired by Sisyphus's answer. Generates three lists of the number string for each of the hundreds, tens and ones. Indexes each list by it's respective value in the input, then gets rid of one-

\$\endgroup\$
2
\$\begingroup\$

JavaScript (Node.js), 147 bytes

n=>['hundred','ten',''].flatMap(t=>(c=',,two,three,four,five,six,seven,eight,nine'.split`,`[p=(n*=10)/1e3%10|0])?t?c+'-'+t:c:p?t||'one':[]).join` `

Try it online!

\$\endgroup\$
2
\$\begingroup\$

C (gcc), 223 bytes

o,i,z;main(a,c)char**c;{for(o=strlen(c[1]);z=c[1][i]-49,printf("%s%s%s ",z|o-i==1?"zero\0one\0two\0three\0four\0five\0six\0seven\0eight\0nine"+"059=CHMQW]"[z+1]-48:"",z&&o+~i?"-":"",o-i-2?o-i-3?"":"hundred":"ten"),++i-o;);}

Try it online!

Thanks to Mukul Kumar for basis, fixed what I perceived to be incorrect output, and shaved down by switching to C and embracing the sketchiness of golf C.

Updates based on issues found and golf contributed by Arnauld

Thanks to ceilingcat for some more C golf sketchiness.

\$\endgroup\$
6
  • \$\begingroup\$ 11 should be ten one and I think 512 should be five-hundred ten two . \$\endgroup\$
    – Arnauld
    Mar 31, 2022 at 21:11
  • \$\begingroup\$ Here is a slightly shorter version based on your original answer. (But still wrong.) \$\endgroup\$
    – Arnauld
    Mar 31, 2022 at 21:13
  • \$\begingroup\$ That's a truly clever edit. But yea, need to work around that one rule. \$\endgroup\$
    – foreverska
    Mar 31, 2022 at 21:15
  • \$\begingroup\$ (Heck. Looks like I added two extra spaces at the end of the code while fighting with my defective keyboard.) \$\endgroup\$
    – Arnauld
    Mar 31, 2022 at 21:17
  • 1
    \$\begingroup\$ @MukulKumar It seems most submissions adopt "number-place" as shown in the testcases rather than the confusing phrasing of the question. Also as Arnauld later pointed out the code didn't handle the one case. \$\endgroup\$
    – foreverska
    Mar 31, 2022 at 22:19
2
\$\begingroup\$

Python 3.8, no library, 184 bytes

Late to the party, but here's my answer. jezza_99 answer using library is unbeatable!

n=input()
for i,o in zip([' one two three four five six seven eight nine'.split(' ')[int(d)]for d in n],['-hundred','-ten',''][-len(n):]):
 if i:print((i+o).replace('one-',''),end=' ')

Try it online!


Commented code

# input: get number to be rewritten in words
# n will be a string
n = input('Input number n to be translated into words (0 < n < 1000): ')

# list of literal digit
# specify split(' ') to have empty string corresponding to 0
literal_digit = ' one two three four five six seven eight nine'.split(' ')
# we will get
# ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']

# list of literal digit corresponding to the input number
# expanded list comprehension [literal_digit[int(d)] for d in n]
literal_number = []
for d in n:
    literal_number.append(literal_digit[int(d)])
# for example '438' -> ['four', 'three', 'eight']

# list of literal orders of magnitude
# we have to consider only the elements according to the length of the number
ord_mag = ['-hundred', '-ten', ''][-len(n):]
# for example '43' -> len = 2 -> start at -2
# we will get only ['-ten', '']

# build the number string translated into words
# loop over the literal number and the corresponding order of magnitude
for i, o in zip(literal_number, ord_mag):
    
    # check if digit is 0 -> '': don't print
    if i:
        
        # not 0
        
        # we must print the literal digit and its order of magnitude
        string = i + o
        
        # if digit is one
        # we must print only the order of magnitude,
        # otherwise only 'one' if the magnitude is unit
        string = string.replace('one-', '')
        # this can be done because the '-' is defined in the order of magnitude
        
        # print the string, concatenating the digit on the same line
        print(string, end=' ')
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 53 bytes

“0€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š“#ISèāR… —¿°¡#sèøʒнa}€á'-ýðý„€µ-K

Try it online or verify all test cases.

Explanation:

“0€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š“
               # Push dictionary string "0 one two ... eight nine"
 #             # Split it on spaces
  I            # Push the input-integer
   S           # Convert it to a list of digits
    è          # Index those into the list
ā              # Push a list in the range [1,length] (without popping)
 R             # Reverse it to range [length,1]
  … —¿°¡       # Push dictionary string "  ten hundred"
        #      # Split it on spaces: ["","","ten","hundred"]
         s     # Swap so the [length,1] integer-list is at the top
          è    # 0-based index into the ["","","ten","hundred"]
ø              # Create pairs of the two lists
 ʒ             # Filter, to only keep the pairs where:
  н            #  the first item
   a           #  only contains letters
               #  (this removes ["0","hundred"], ["0","ten"], and ["0",""] pairs)
 }€            # After the filter: map over each remaining pair:
   á           #  Only keep strings consisting solely of letters
               # (this removes the "" from the last pair)
    '-ý       '# Join each inner pair with "-" delimiter
       ðý      # Then join these strings with " " delimiter
         „€µ-  # Push dictionary string "one-"
             K # Remove all those substrings
               # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to use the dictionary?) to understand why “0€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š“ is "0 one two three four five six seven eight nine"; … —¿°¡ is " ten hundred"; and „€µ- is "one-".

\$\endgroup\$
2
  • \$\begingroup\$ I think you mean 48 bytes. 53 is the test suite byte count lol \$\endgroup\$
    – lyxal
    Mar 31, 2022 at 6:48
  • \$\begingroup\$ @lyxal No, it's 53 bytes. I simply forgot to update the TIO when I added the trailing "one-" removal. Thanks for noticing, I've updated the single TIO. \$\endgroup\$ Mar 31, 2022 at 6:49
1
\$\begingroup\$

Retina 0.8.2, 102 bytes

\B
-ten¶
ten(¶..)
hundred$1
A`0
¶
 
1-

1
one
2
two
3
three
4
four
5
five
6
six
7
seven
8
eight
9
nine

Try it online! Link includes test cases. Explanation:

\B
-ten¶

If there is more than one digit then assume the leading digit is a ten. Also split the digits onto their own line.

ten(¶..)
hundred$1

If there's a second digit then correct it to a hundred.

A`0

Delete any lines containing a 0.

Join the lines together again.

1-

10 and 100 should just be ten and hundred.

1
one
2
two
3
three
4
four
5
five
6
six
7
seven
8
eight
9
nine

Turn the remaining digits into words.

Bonus: Here's a version that works up to 9999 for 116 bytes: Try it online! Link includes test cases. (Sadly the three digit version is 103 bytes.)

\$\endgroup\$
1
\$\begingroup\$

Python 2, 209 bytes

i=input();t,n="0 one two three four five six seven eight nine".split(),map(int,"00"+`i`)[-3:]
print ("",t[n[0]]+"-hundred ")[n[0]and i>99]+("",(t[n[1]]+"-","")[n[1]<2]+"ten ")[n[1]and i>9]+("",t[n[2]])[n[2]>0]

Try it online!

Python 2 because:

  1. Accepts integers directly as input

  2. `` converts integer to string

  3. Can directly index the result of map without converting to a list

  4. No () required by the print

\$\endgroup\$
1
\$\begingroup\$

PHP, 195 bytes

<?php foreach(str_split(substr("00".fgets(STDIN),-3))as$i=>$c)echo trim($c>0?[0,$i>1?"one":"","two","three","four","five","six","seven","eight","nine"][$c].["-hundred","-ten",""][$i]." ":"","-");

Input via STDIN, output via echo

Try it online!


De-golf:

<?php
    $inp = substr("00".fgets(STDIN),-3);
        // here I take input and append leading zeros, so as the total lengh is 3
        //   (for example, 13 becomes 013)
    $arr = str_split($inp);
        // string to array of chars
    
    foreach($arr as $index => $digit){
        $num_array = [0, $index > 1 ? "one":"","two","three","four","five","six","seven","eight","nine"]; // here one is hidden unless it's on the last position
        $name_array = ["-hundred","-ten",""];
        $chunk = $num_array[$digit].$name_array[$index]." ";
        $chunk = $digit > 0 ? $chunk : ""; // empty string if it's zero
        $trimmed = trim($chunk, "-"); // to dispose of "-" at the beginning of the string (this could happen if we drop "one" in "one-ten", so just "-ten" left)
        echo $trimmed;
    }
?>

Fun note - I didn't expect PHP doesn't complain here:

enter image description here

\$\endgroup\$
0
1
\$\begingroup\$

C++, 277 bytes

#include <iostream>
main(int a,char*c[]){char s[9];int q[]={0,4,7,10,15,19,23,26,31,36,40},n,m,o=strlen(c[1]),i=0;do{m=c[1][i]-48;n=q[m];strcpy_s(s,"zeroonetwothreefourfivesixseveneightnine"+n,q[m+1]-n);std::cout<<((o-i==2)?"ten-":(o-i==3)?"hundred-":"")<<s<<' ';}while(++i<o);}

Ungolfed

#include <iostream>

int main(int a, char* c[]) {
    char s[9];
    int q[] = { 0,4,7,10,15,19,23,26,31,36,40 }, n, m, o = strlen(c[1]), i = 0;
    do {
        m = c[1][i] - 48;
        n = q[m];
        strncpy_s(s, 9, "zeroonetwothreefourfivesixseveneightnine" + n, q[m + 1] - n);
        std::cout << ((o - i == 2) ? "ten-" : (o - i == 3) ? "hundred-" : "") << s << ' ';
    } while (++i < o);
}  

Input comes from command-line arguments

Reason for using C++ over C

  • apparantly some compilers include string.h header in iostream

enter image description here

\$\endgroup\$
1
1
\$\begingroup\$

Julia, 185 158 bytes

n+m+s=!(n÷m)*"-$s "*!(n%m)
!n=replace(n<10 ? split(" one two three four five six seven eight nine"," ")[-~n] :
n<100 ? n+10+:ten : n+100+:hundred,"one-"=>"")

Attempt This Online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.