36
\$\begingroup\$

CodeGolf Challenge

PWSSHHHH! You wake up in a cryogenics lab in the year 3000. Upon being escorted to the assignment office to receive your career chip, presumably that of a delivery boy, a probe detects that you are from the year 2000. Because of this, and a few stereotypes, you are assumed stupid compared to today's modern human and are forced to repeat gradeschool.

You enter your first grade classroom and the teacher is giving an assignment. She will say or write a number up to 50. If she writes the number on the board (for example: 25) then you have to say the numbers up to that number "one, two, three, ..., twenty-five". If she says the number out loud (for example: "six") then, on your tablet, you have to write the numbers up to that number "1, 2, 3, 4, 5, 6"

This becomes very tedious and you decide you will automate the process with your still functioning, yet archaic, 21st century programming knowledge.


Objective:

Your program should take an input. This input will either be a decimal number (1 thru 50) or a written-out number (one thru fifty).

•If the input is a decimal number, your output should count from one to said number, using written-out style. (e.g. thirty-two)

•If the input is a written-out number, your output should count from 1 to said number, using decimal style. (e.g. 32)


Rules:

Input and Output can be in any case of your choosing (so you can make a program that only accepts upper-case if desired).

Input decimal numbers do not have to be of a number type (e.g. int), they can be a input string containing numbers (25 vs "25"). Either are fine and you can chose which one you want your program to accept. (Your program does not need to accept both)

Written out style does NOT require a hyphen between compound words, but you can if desired.

Output values have to be separated in some form, any separator is fine 1,2,3 1 2 3 etc

You can not add extra libraries like num2words (python) etc (However system libraries are fine)

Even though the back-story says you are from the year 2000, you can use languages created after that date (lol)


This is , so the program with the shortest bytecount wins!

\$\endgroup\$
11
  • 1
    \$\begingroup\$ Are we allowed to use libraries like num2words in case of python. \$\endgroup\$ Jan 31, 2017 at 9:56
  • 1
    \$\begingroup\$ @AlbertRenshaw but what about builtins that do that? (Mathematica) \$\endgroup\$
    – Pavel
    Jan 31, 2017 at 18:06
  • 1
    \$\begingroup\$ @coredump Either means you can pick one or the other or both. It doesn't have to be able to handle both kinds of inputs \$\endgroup\$ Jan 31, 2017 at 19:43
  • 2
    \$\begingroup\$ "Bite my shiny metal ass!" I won't count myself \$\endgroup\$
    – Penguin9
    Feb 1, 2017 at 13:22
  • 1
    \$\begingroup\$ I keep thinking the title is "your first (grade counting)" not "your (first grade) counting" \$\endgroup\$
    – CAD97
    Feb 3, 2017 at 14:36

18 Answers 18

31
\$\begingroup\$

Perl 6, 119 113 bytes

{my \n=<① ⑳ ㉑ ㉟ ㊱ ㊿>.map:{|map *.uniname.words[2..*].join,$^a..$^b}
/\d/??n[^$_]!!1..1+first $_,n,:k}

Unicode database FTW!

Uses upper-case written-out numbers without hypen, e.g. TWENTYTWO.
Returns a list of strings, or a range of numbers. (Both use space as separator when printed with put.)

\$\endgroup\$
1
  • 3
    \$\begingroup\$ Very clever ahahaha! Love it \$\endgroup\$ Jan 31, 2017 at 19:44
13
\$\begingroup\$

Python3, 276 271 269 243 237 235 232 217 bytes

Taking a cue from the @smls perl submission...

from unicodedata import*
o=[name(chr(k)).split(' ',2)[-1]for j in['①⑴','㉑㉠','㊱㋀']for k in range(ord(j[0]),ord(j[1]))]
i=input()
w=i in o
for i in range(w and o.index(i)+1or int(i)):print(w and i+1or o[i])

I suspect it might be golfed a little bit further.

It makes use of the system library unicodedata to look up names for numbers. It requires upper case number names (separated by space: FORTY TWO) or decimal integers as input.

(This is my first code golf submission.)

(I also just noticed I was miscounting the length (encoding), so it's a few bytes less than previously thought. I've only updated the most recent byte count, though. Oops.)

\$\endgroup\$
8
  • \$\begingroup\$ Welcome to PPCG! \$\endgroup\$ Jan 31, 2017 at 17:51
  • \$\begingroup\$ In advocacy: unicodedata is a system library that comes with the default installation, not an "extra" library that needs to be installed separately. \$\endgroup\$
    – kuupaork
    Jan 31, 2017 at 17:54
  • \$\begingroup\$ Welcome to the site! You can remove lots of spaces from your code. \$\endgroup\$
    – xnor
    Jan 31, 2017 at 18:16
  • 1
    \$\begingroup\$ Welcome to PPCG. You can lose 3 bytes by putting the print inside a for loop and having newlines between each output. print() doesn't care if it is an integer or a string then. Try it online! \$\endgroup\$
    – ElPedro
    Jan 31, 2017 at 22:38
  • 1
    \$\begingroup\$ I think you can import* instead of import name to save a couple of bytes \$\endgroup\$
    – Wheat Wizard
    Feb 2, 2017 at 17:54
10
\$\begingroup\$

Common Lisp, 297 253 243 242 144 128

(lambda(s)(#1=dotimes(u(or(#1#(i 51)(if(equal(#2=format()"~R"i)s)(return i)))s))(#2#t"~[~:;, ~]~:[~R~;~D~]"u(stringp s)(1+ u))))

Details

(lambda (s) 
  (dotimes                         ; iterate...                                                                          
      (u                           ; for u from zero below ...                
       (or                         ; if s is a string, then                   
        (dotimes (i 51)            ;   parse s by iterating from 0 to 50      
          (if (equal               ;   until we find a match between          
               (format nil "~R" i) ;   the English word(s) for i              
               s)                  ;   and the given s                        
              (return i)))         ;   (exit loop)                            
        s))                        ; otherwise, use s, which is a number      
    (format t                      ; for each U, print to standard output     
            "~[~:;, ~]~:[~R~;~D~]" ; (see below for details)                  
            u                      ; ...                                      
            (stringp s)            ; ... arguments to format                  
            (1+ u))))              ; ...                                      
  • ~[ 0 ~; 1 ~; ... ~:; else ~] is a switch, based on the next available argument's value, which jumps to the appropriate sub-control format. Here, I only have a case of "0" and for "else". This is used to insert a separator before each number except the first one, thanks to U starting from zero.

  • ~:[ FALSE ~; TRUE ~] is a conditional format; here we output things differently whether the input s is a string or not.

  • ~R write a number as a cardinal English number, whereas ~D simply prints the number.

Examples

CL-USER> (test "five")
1, 2, 3, 4, 5

CL-USER> (test 32)
one, two, three, four, five, six, seven, eight, nine, ten, eleven, twelve, thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen, twenty, twenty-one, twenty-two, twenty-three, twenty-four, twenty-five, twenty-six, twenty-seven, twenty-eight, twenty-nine, thirty, thirty-one, thirty-two
\$\endgroup\$
6
  • \$\begingroup\$ From what I understand of the question, you have to be able to parse both styles, not just one, so your 55 byte solution might not be valid. "Your program does not need to accept both" is referring to 25 vs "25", the decimal as a number or as a string. \$\endgroup\$
    – Tom
    Feb 1, 2017 at 9:23
  • \$\begingroup\$ @TomDevs Thanks. This is definitely confusing. To be sure, if I define f so that "(f 2)" prints "one, two" and (f "two") prints "1, 2", does it look good to you? \$\endgroup\$
    – coredump
    Feb 1, 2017 at 9:30
  • \$\begingroup\$ Yep, I think that's right. \$\endgroup\$
    – Tom
    Feb 1, 2017 at 9:37
  • \$\begingroup\$ @TomDevs Thanks, I fixed it \$\endgroup\$
    – coredump
    Feb 1, 2017 at 10:09
  • 1
    \$\begingroup\$ @AlbertRenshaw No, only English; this feature might be already considered as bloat, but since it was already implemented in some Lisps, it was standardized. \$\endgroup\$
    – coredump
    Feb 15, 2017 at 7:01
8
\$\begingroup\$

JavaScript ES6, 559 526 381 368 364 358 332 327 315 bytes

a="one0two0three0four0five0six0seven0eight0nine0ten0eleven0twelve0thir10four10fif10six10seven10eigh10nine1".replace(/1/g,'teen').split(0),b="twenty0thirty0forty0fifty".split(0),c=(n,d=Array,e=b.forEach(i=>a=a.concat(i,a.slice(0,9).map(x=>i+x))))=>1/n?a.slice(0,n).join():d.from(d(a.indexOf(n)+1),(x,i)=>i+1).join();

Thanks to Kritixi Lithos for the idea of splitting the array and Arnauld for the 1/n trick.

a="one0two0three0four0five0six0seven0eight0nine0ten0eleven0twelve0thir10four10fif10six10seven10eigh10nine1".replace(/1/g,'teen').split(0),b="twenty0thirty0forty0fifty".split(0),c=(n,d=Array,e=b.forEach(i=>a=a.concat(i,a.slice(0,9).map(x=>i+x))))=>1/n?a.slice(0,n).join():d.from(d(a.indexOf(n)+1),(x,i)=>i+1).join();

console.log(c("twentyfive"));
console.log(c("fifty"));
console.log(c(50));

\$\endgroup\$
5
  • 1
    \$\begingroup\$ You can remove the var and you can change the array ['one,'two',..] to "one0two0three0...".split(0) \$\endgroup\$
    – user41805
    Jan 31, 2017 at 11:24
  • \$\begingroup\$ Redundant whitespace at null, Array(n). \$\endgroup\$
    – Yytsi
    Jan 31, 2017 at 11:40
  • 2
    \$\begingroup\$ You can replace !isNaN(n) by 1/n. This gives you NaN for a string (falsy), a non-zero float for a non-zero integer (truthy), or Infinity for 0 (also truthy). \$\endgroup\$
    – Arnauld
    Jan 31, 2017 at 13:43
  • \$\begingroup\$ Add 4 spaces infront of every code line \$\endgroup\$
    – sagiksp
    Jan 31, 2017 at 14:06
  • \$\begingroup\$ @sagiksp Yeah, must have messed something up when editing the post, should be fixed now :) \$\endgroup\$
    – Tom
    Jan 31, 2017 at 14:15
6
\$\begingroup\$

Python 2, 503 499 494 490 479 bytes

-5 with thanks to @JonathanAllan

l='one two three four five six seven eight nine ten eleven twelve thir#four#fif#six#seven#eigh#nine#'.replace('#','teen ').split()
m='twenty','thirty','forty','fifty'
i,z,R=raw_input(),' ',range
try:n=int(i);p=(n/10)-2;o=(l+sum([[m[x]]+[m[x]+z+l[y]for y in R(9)]for x in R(p)],[])+[m[p]]+[m[p]+z+l[y]for y in R(n%10)],l[:n])[n<20]
except:j=i.split();o=map(str,R(1,(m.index(j[0])+2)*10+l.index(j[1])+2if z in i else l.index(i)+2if i in l else(m.index(i)+2)*10+1))
print','.join(o)

Try it online!

Input either a number or a space seperated spelling of a number.

Slightly less golfed and more readable version:

l='one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen'.split()
m='twenty','thirty','forty','fifty'
i=raw_input()
try:
 n=int(i)
 if n<20:
  o=l[0:n]
 else:
  o=l
  for x in range((n/10)-2):
   o+=[m[x]]+[m[x]+' '+l[y]for y in' '*9]
  p=m[(n/10)-2]
  o+=[p]+[p+' '+l[y]for y in' '*n%10]
except:
 if' 'in i:
  t=i.split()
  s=((m.index(t[0])+2)*10)+l.index(t[1])+2
 else:
  s=l.index(i)+2 if i in l else((m.index(i)+2)*10)+1
 r=range(1,s)
 o=map(str,r)
print','.join(o)
\$\endgroup\$
4
  • 1
    \$\begingroup\$ 6 byte save with l="one two three four five six seven eight nine ten eleven twelve thir#four#fif#six#seven#eigh#nin#".replace("#","teen ").split() \$\endgroup\$ Jan 31, 2017 at 15:40
  • \$\begingroup\$ ...oops 5, missed the e from nineteen. \$\endgroup\$ Jan 31, 2017 at 15:47
  • \$\begingroup\$ Is there a reason you need to use Python 2, without it the print would be longer, but the raw_input could just be input? (Same question for your other answer) \$\endgroup\$
    – nedla2004
    Feb 1, 2017 at 0:30
  • \$\begingroup\$ @nedla2004 - No reason other than that I haven't got round to installing Python 3 on my latest laptop yet :-) \$\endgroup\$
    – ElPedro
    Feb 1, 2017 at 8:10
6
\$\begingroup\$

Scheme, 161, 152, 149

(define (c x)(let((r(string->number x)))(let l((i 1))(let((n (format #f "~r" i)))(display(if r n i))(newline)(or(eq? r i)(equal? x n)(l (+ i 1)))))))

Uncompressed:

(define (count limit)
  (let ((numerical-limit (string->number limit)))
    (let l ((i 1))
      (let ((current-number (format #f "~r" i)))
        (display (if numerical-limit current-number i))
        (newline)
        (or (eq? numerical-limit i)
            (equal? limit current-number)
            (l (+ i 1)))))))
\$\endgroup\$
4
  • \$\begingroup\$ How do you convert from e.g. "four" to 4? I am not sure string->number does this, I checked quickly and it seems to be used to convert e.g. from string "4" to number 4. \$\endgroup\$
    – coredump
    Jan 31, 2017 at 12:20
  • \$\begingroup\$ @coredump That is correct. (string->number "four") returns #f. \$\endgroup\$ Jan 31, 2017 at 12:58
  • \$\begingroup\$ Which Scheme are you running? \$\endgroup\$
    – coredump
    Jan 31, 2017 at 19:12
  • 1
    \$\begingroup\$ @coredump guile 2.0.9 \$\endgroup\$ Feb 1, 2017 at 6:25
6
\$\begingroup\$

PHP - 397 372 349 344 329 bytes

Inspired by TomDevs's JS solution

Saved 25 bytes by replacing $a=[...] by $a=explode(...)

Saved another 23 bytes by switching back to an array without string delimiters and by storing teen in a variable, thanks to @user59178

Saved another 5 bytes by removing the (int) typecasting

Saved another 15 bytes by dropping $b, the $i in the for declarations, and the curly braces, thanks to @user59178 again

$a=[one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thir.$t=teen,four.$t,fif.$t,six.$t,seven.$t,eigh.$t,nine.$t];foreach([twenty,thirty,forty,fifty] as$c){$a[]=$c;for($i=0;$i<9;)$a[]=$c.'-'.$a[$i++];}if($argv[1]!=0)for($i=0;$i<$argv[1];)echo$a[$i++].' ';else for($i=1;$i<=array_search($argv[1],$a)+1;)echo$i++.' ';

Ungolfed :

$a =[one,two,three,four,five,six,seven,eight,nine,ten,eleven,‌​twelve,thir.$t=teen,‌​four.$t,fif.$t,six.$‌​t,seven.$t,eigh.$t,n‌​ine.$t];
foreach ([twenty,thirty,forty,fifty] as $c){
    $a[] = $c;
    for ($i=0;$i<9;)
        $a[] = $c . '-' . $a[$i++];
}
if( $argv[1] !=0 )
    for ($i=0;$i<$argv[1];)
        echo $a[$i++] . ' ';
else
    for ($i=1;$i<=array_search($argv[1], $a)+1;)
        echo $i++ . ' ';

Try it for an input string or for an input number

\$\endgroup\$
9
  • 1
    \$\begingroup\$ When golfing you can use many strings directly without any quotes, including all the number you use. This causes a notice but that can be ignored. Additionally it's shorter (by 2 whole bytes) to store teen in a variable rather than repeating it each time. As such it would become: $a=[one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thir.$t=teen,four.$t,fif.$t,six.$t,seven.$t,eigh.$t,nine.$t]; \$\endgroup\$
    – user59178
    Jan 31, 2017 at 14:55
  • \$\begingroup\$ Hadn't thought of that, thanks ;) \$\endgroup\$
    – roberto06
    Jan 31, 2017 at 15:19
  • \$\begingroup\$ You can save 7 more bytes by dropping $b and putting the second array directly into the foreach, 6 more bytes by dropping all the curly braces (though you need to put the $a=$c into the setup of the for loop) and 6 more bytes by post-incrementing $i when you use it rather than in the 'after' bit of the for loops. \$\endgroup\$
    – user59178
    Jan 31, 2017 at 15:39
  • \$\begingroup\$ Save six bytes (two per loop) by moving the post-increment from the for to the following line: for ($i=0;$i<9;)$a[]=$c.'-'.$a[$i++]; \$\endgroup\$ Jan 31, 2017 at 16:17
  • \$\begingroup\$ Ooops, sorry, just noticed that @user59178 suggested the same thing... \$\endgroup\$ Jan 31, 2017 at 16:18
6
\$\begingroup\$

Python 2, 262 bytes

x="one two three four five six seven eight nine ten eleven twelve thir#four#fif#six#seven#eigh#nine#".replace("#","teen ").split()
x+=[a+"ty"+b for a in"twen","thir","for","fif"for b in['']+x[:9]]
v=input()
for s in range(1,x.index(v)+2)if v>50else x[:v]:print s

repl.it

Input and output strings are lower case and concatenated*, so to test a string input enter, for example, "thirtyfive" at the prompt.

Builds the list of all the words (plus "fiftyone" to "fiftynine"), x, then tests if input is a word with the proxy v>50 (strings are greater than numbers in Python 2, and all the numbers in the valid input range from the specification are <=50) and prints the appropriate values by either slicing the list, x[:v], or building a range of integers, range(1,x.index(v)+2).

* Adding hyphenation on both costs 11 bytes, by replacing a+"ty"b with a+"ty"+'-'*(b>'')+b.

\$\endgroup\$
5
\$\begingroup\$

Wolfram Language, 92 bytes

If[NumberQ@#, Do[Print@IntegerName@i, {i, #}], 
  Do[Print@i, {i, Interpreter["SemanticNumber"]@#}]] &

(I'm new to this, let me know if I did something wrong)

\$\endgroup\$
1
  • 2
    \$\begingroup\$ -10 bytes: Do[Print@If[#>0,i,,IntegerName@i],{i,If[#>0,#,,Interpreter["SemanticNumber"]@#]}]& \$\endgroup\$ Feb 1, 2017 at 2:01
5
\$\begingroup\$

JavaScript (ES6), 261 bytes

Note: the string assigned to z is encoded with atob. In the encoded string there are 11 bytes that I can not post to this site, even if they are valid characters in a javascript string. So I used an hex escape in the form \xHH. Each one of these escapes is counted as 1 byte.
The original uncompressed string is the less golfed version.

x=>(z=btoa('ö\x89ÞöÜ(öØkyï_¢êý~+Þöȱöǯz\x7f^\x8a\x08möx§{Û^\x9f×¥z÷§öÜ\x1e\x96÷½¶\x18«÷×â\x7fß}z(!÷Ûpz\x7f}~\x8aý').split(9),o=(0+z.map((v,i)=>i<20?i<13?v:(v||z[i-10])+'teen':z.slice(0,10).map(d=>(z[i]||z[i-8]||z[i-18])+'ty'+d))).split`,`,p=o.indexOf(x),o.slice(1,-~x+p+!~p).map((x,i)=>~p?i+1:x))

Less golfed

x => (
  z = '9one9two9three9four9five9six9seven9eight9nine9ten9eleven9twelve9thir99fif999eigh99twen99for9'
      .split(9),
  o = (0 + // 0 + array to build a comma separated string
       z.map( (v, i) => 
         i < 20 
         ? i < 13 
           ? v // 1 to 13 are 'as is'
           : (v||z[i-10])+'teen' // compose for 14 to 19
         : z.slice(0,10).map(d=>(v||z[i-8]||z[i-18])+'ty'+d)) // 20s, 30s, 40s, 50s
      ).split`,`, // from comma separated to array again
  // o contains strings from one to fiftynine
  p = o.indexOf(x), // look for input
  o.slice(1, -~x+p+!~p).map((x,i) => ~p?i+1:x)
)

Test

F=
x=>(z=btoa('ö\x89ÞöÜ(öØkyï_¢êý~+Þöȱöǯz\x7f^\x8a\x08möx§{Û^\x9f×¥z÷§öÜ\x1e\x96÷½¶\x18«÷×â\x7fß}z(!÷Ûpz\x7f}~\x8aý').split(9),o=(0+z.map((v,i)=>i<20?i<13?v:(v||z[i-10])+'teen':z.slice(0,10).map(d=>(v||z[i-8]||z[i-18])+'ty'+d))).split`,`,p=o.indexOf(x),o.slice(1,-~x+p+!~p).map((x,i)=>~p?i+1:x))

function update() {
  var i=I.value
  O.textContent = F(i)
}  

update()
<input id=I value=25 oninput='update()'><pre id=O></pre>

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

Python 3, 305 303 bytes

Converted to Python 3 after advice from @nedla2004. Now also has no space between written numbers on input or output e.g. enter twentytwo

l='one two three four five six seven eight nine ten eleven twelve thir#four#fif#six#seven#eigh#nine#'.replace('#','teen ').split()
m='twenty','thirty','forty','fifty'
i,R=input(),range
l+=sum([[m[x]]+[m[x]+l[y]for y in R(9)]for x in R(3)],[])
for x in R(1,l.index(i)+2)if i in l else l[:int(i)]:print(x)

Try it online 3!

Python 2, 327 320 313 308 bytes

l='one two three four five six seven eight nine ten eleven twelve thir#four#fif#six#seven#eigh#nine#'.replace('#','teen ').split()
m='twenty','thirty','forty'
i,R=raw_input(),range
l+=sum([[m[x]]+[m[x]+l[y]for y in R(9)]for x in R(3)],[])+['fifty']
for x in R(1,l.index(i)+2)if i in l else l[:int(i)]:print x

Try it online 2!

163 170 177 bytes shorter than my original answer so I am posting it as an alternative. This uses for on the two lists to build up a complete list of all of the string representations of the numbers then identifies the right one in the list and prints everything up to it either by value or by index. Outputs a new line for each value.

\$\endgroup\$
5
\$\begingroup\$

Python 2, 432 422 416 403 bytes

I'm sure this can be improved on. At the very least if I can get away with hardcoding the value to be worked on and not needing a function I can save 20. It needs a space to separate words in text input. Saved 6 bytes thanks to JonathanAllan's comment on ElPedro's answer, 4 for rearranging maths.

def z(f):
 a,b,i,d="one two three four five six seven eight nine ten eleven twelve thir#four#fif#six#seven#eigh#nine#".replace("#","teen ").split()+[""],"twenty thirty forty fifty".split(),1,f>50
 if d:f=f.split();f=a.index(f[-1])+21+b.index(f[-2])*10 if len(f)>1 else b.index(f[-1])*10+20 if f[-1]in b else a.index(f[-1])+1
 while i<=f:s=i if d else a[i-1]if i<20 else b[i//10-2]+a[i%10-1];print s;i+=1

(NB: The actual version of this uses tabs to indent instead of spaces. QPaysTaxes added a single space because that wasn't rendering properly, to ensure that the given code compiles. It shouldn't change the byte count.)

\$\endgroup\$
18
  • \$\begingroup\$ @JonathanAllan's comment on ElPedro's answer works here too for -6 \$\endgroup\$
    – Chris H
    Jan 31, 2017 at 16:02
  • 1
    \$\begingroup\$ len(`f`)>2 can be ...`f`[2:] for another 3 I believe. (ignore the ... can't seem to get the backticks working properly) \$\endgroup\$ Jan 31, 2017 at 16:13
  • \$\begingroup\$ In fact in python 2 you could go for f>50 for 6. (and another one by not using d) \$\endgroup\$ Jan 31, 2017 at 16:17
  • \$\begingroup\$ @JonathanAllan that doesn't work for passing numbers as integers, which I currently do: TypeError: 'int' object has no attribute '__getitem__'. If I pass numeric input as a string, f[2:] gets closer but still fails when trated as a boolean (print f[2:] and True prints a blank line if len(f)<2, not True or False) \$\endgroup\$
    – Chris H
    Jan 31, 2017 at 16:25
  • \$\begingroup\$ @JonathanAllan f>50 works, thank you. Dropping d isn't so simple as I always put the end value of the loop into f so line 8 can't be changed to if f>50 as it will never be true. \$\endgroup\$
    – Chris H
    Jan 31, 2017 at 16:29
4
\$\begingroup\$

C++11, 484 480 477 bytes

#import<iostream>
#import<cstdlib>
#import<vector>
using namespace std;f(){int j,i=2;string s="teen";vector<string>v={"","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"};for(;i++<9;)v.push_back(v[i]+s);v[13]="thir"+s;v[15]="fif"+s;v[18]="eigh"+s;for(i=19;i++<50;){string n[4]={"twenty","thirty","forty","fifty"};v.push_back(n[i/10-2]+v[i%10]);}cin>>s;if(i=atoi(s.c_str()))for(j=0;j++<i;)cout<<v[j]<<" ";else while(v[i++]!=s)cout<<i<<" ";}

Text input in lower-case without hyphens.

\$\endgroup\$
3
\$\begingroup\$

PowerShell, 362 bytes

$z=0..50|%{("0twenty0thirty0forty0fifty"-split0)[+(($b="$_"[0])-gt49)*($_-gt19)*(+"$b"-1)]+($x=(("0one0two0three0four0five0six0seven0eight0nine0ten0eleven0twelve"-split0)+(-split'thir four fif six seven eigh nine'|%{$_+'teen'})))[($_%10)*($_-gt19)]+$x[$_*($_-le19)]}
if(($n=-split$args)[0][0]-in48..57){$z[$n[0]..$n[2]]}else{$z.IndexOf($n[0])..$z.IndexOf($n[2])}

Try it online! words input or numbers input

This is a right mess, and I'm not terribly happy with it, but here it is. Golfing suggestions welcome.

The first line sets $z to be an array of the full English words. You can see the -split0 for numbers 1 to 12, and the loop to construct all the teens, and then there's a bunch of logic to put everything together right. Try it online!

The second line starts with some logic. We take the input $args (as a string), -split it on whitespace, store it into $n for use later, take the first [0] word, and the first [0] character of that, and check if it is -in a range 48..57 (i.e., ASCII 0 to 9). So, we're checking if we have decimal input or English input. Try it online!

In the first case, we build a range based on the decimal inputs $n[0]..$n[2] and use that to index into $z[...]. In the other case, we find the .indexOf() the first word and the last word, and build just a numerical range from that. In either situation, we now have an array of objects on the pipeline (either strings or integers), and an implicit Write-Output at program completion gives us a newline between elements.

\$\endgroup\$
3
\$\begingroup\$

Swift3, 402 bytes

let f=["one","two","three","four","five","six","seven","eight","nine"]
let g=["twenty","thirty","forty","fifty"]
let v=[f,["ten","eleven","twelve"],["thir","four","fif","six","seven","eigh","nine"].map{$0+"teen"},[g[0]],f.map{g[0]+$0},[g[1]],f.map{g[1]+$0},[g[2]],f.map{g[2]+$0},[g[3]]].flatMap{$0}
func c(s:String){if let i=Int(s){print(v.prefix(upTo:i))}else{for j in 1...v.index(of:s)!+1{print(j)}}}

Ungolfed:

let f = ["one","two","three","four","five","six","seven","eight","nine"]
let g = ["twenty","thirty","forty","fifty"]

let values = [f,["ten","eleven","twelve"],["thir","four","fif","six","seven","eigh","nine"].map{$0+"teen"},
              [g[0]], f.map{g[0]+$0},
              [g[1]], f.map{g[1]+$0},
              [g[2]], f.map{g[2]+$0},
              [g[3]]].flatMap{$0}

func count(s:String){
    if let i = Int(s) {
        print(values.prefix(upTo: i))
    } else {
        for j in 1...values.index(of: s)!+1{
            print(j)
        }
    }
}

count(s:"29")
count(s:"twentyeight")

Nothing special here, just using an array to back up the written-out numbers.

I originally thought this solution using this other way to calculate the values array:

let values = f + ["eleven","twelve"]
    + ["thir","four","fif","six","seven","eigh","nine"].map{$0+"teen"}
    + [g[0]] + f.map{g[0]+$0}
    + [g[1]] + f.map{g[1]+$0}
    + [g[2]] + f.map{g[2]+$0}
    + [g[3]]

Which could be golfed to:

let v=f+["eleven","twelve"]+["thir","four","fif","six","seven","eigh","nine"].map{$0+"teen"}+[g[0]]+f.map{g[0]+$0}+[g[1]]+f.map{g[1]+$0}+[g[2]]+.map{g[2]+$0}+[g[3]]

replacing the 3rd line in the golfed code

I could have scored 381 bytes, but, there is a compiler error that says: "expression was too complex to be solved in reasonable time", more info on the error can be found here

\$\endgroup\$
1
  • \$\begingroup\$ LOVE seeing swift in here, I'll have to check this out more when I get back \$\endgroup\$ Feb 1, 2017 at 1:00
3
\$\begingroup\$

R, 452 430 424 bytes

o=c("","one","two","three","four","five","six","seven","eight","nine") 
t=gsub(0,"teen",c("ten","eleven","twelve","thir0","four0","fif0","six0","seven0","eigh0","nine0"))
s=c("twenty","thirty","forty") 
p=""
for(i in s){for(j in o){p=paste0(p,i,j," ")}}
as.data.frame(t(d<-1:50))
names(d)=c(o[-1],t,as.vector(strsplit(p," ")[[1]]),"fifty")
f=function(x){if(is.numeric(x)){names(d)[1:x]}else{matrix(d[1:d[x]],dimnames=NULL)}}

#> f(5)
#[1] "one"   "two"   "three" "four"  "five" 

#> f('five')
#     [,1]
#[1,]    1
#[2,]    2
#[3,]    3
#[4,]    4
#[5,]    5

Places the numbers in a data.frame with written-out numbers as column names, making the translation between the two (and subsequent printing) pretty easy.

Main attempt at golfing was in creating the written-out numbers for 20-49, probably much more to golf here.

I made an attempt with as.matrix to print the data.frame with just the numbers, but am still left with a matrix header. Hopefully that's ok.

Ungolfed:

ones <- c("","one","two","three","four","five","six","seven","eight","nine") 
teens <- c("ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen")
tens <- c("twenty","thirty","forty") 

p=""
for(i in tens){
  for(j in ones){
    p=paste0(p, i, j," ")
  }
}

nums <- 1:50
as.data.frame(t(nums))
names(nums) <- c(ones[-1], teens, as.vector(strsplit(p, " ")[[1]]), "fifty")
f <- function(x){
  if(is.numeric(x)){
    names(nums)[1:x]
  } else {
    matrix(nums[1:nums[x]], dimnames = NULL)
  }
}
\$\endgroup\$
2
  • \$\begingroup\$ Slight improvement to 359 Bytes: o=c("","one","two","three","four","five","six","seven","eight","nine") ; v=c("ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"); w=c("twenty","thirty","forty"); a=data.frame(0:50, c(o,v, sapply(w[1:3],function(y) sapply(o,function(x) paste0(y,x))),"fifty")); b=which(a==i); a[if(b<52) 2:b else 2:(b-51),ifelse(b<52,2,1)] \$\endgroup\$
    – count
    Feb 1, 2017 at 15:02
  • \$\begingroup\$ @count looks like a big improvement! I can't seem to figure out where the function is, though, or where you'd take an argument. \$\endgroup\$
    – BLT
    Feb 1, 2017 at 15:49
2
\$\begingroup\$

C, 342 331 bytes

char*x[]={"teen","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve","thir","four","fif","twenty","thirty","fourty","fifty"};void main(int z,char**i){for(z=1;z<=atoi(i[3]);z++)printf("%s%s%s\n",z<16?x[z]:z<20?z^18?x[z-10]:"eigh":x[z/10+14],z>20&&z%10?"-":z>12&&z<20?*x:"",z>20&&z%10?x[z%10]:"");}

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ My favorite language :) \$\endgroup\$ Feb 4, 2017 at 9:36
  • 1
    \$\begingroup\$ You actually don't need the 1 thru or one thru; all this codegolf requires is your third argument. The first two will always be "1 and thru" (or "one and thru") \$\endgroup\$ Feb 4, 2017 at 9:38
  • \$\begingroup\$ @AlbertRenshaw Good call! Thanks :) \$\endgroup\$
    – Ahemone
    Feb 4, 2017 at 11:05
1
\$\begingroup\$

SAS, 179

%macro c(n);%let f=words.;%if%length(&n)>2%then%do;%do c=1%to 50;%if%qsysfunc(putn(&c,&f))=&n%then%let n=&c;%end;%let f=2.;%end;%do i=1%to &n;%put%sysfunc(putn(&i,&f));%end;%mend;

Output is written to the log separated by newlines. SAS has a built-in format for converting digits to words, which is a major advantage for this challenge, but annoyingly it lacks an informat for doing the reverse.

\$\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.