| Bytes | Lang | Time | Link |
|---|---|---|---|
| 246 | PHP7.4 d error_reporting=0 | 241129T144829Z | Lux |
| 341 | Haskell | 230417T185456Z | Roman Cz |
| 108 | 05AB1E | 230411T134550Z | Kevin Cr |
| 213 | JavaScript ES6 | 230409T184646Z | Arnauld |
| 157 | Retina 0.8.2 | 230410T000049Z | Neil |
| 427 | Mathematica | 230410T082116Z | 138 Aspe |
| 091 | Python + num2words | 230409T173613Z | The Thon |
| 127 | Charcoal | 230409T221723Z | Neil |
PHP7.4 -d error_reporting=0, 246 bytes
fn($n)=>strtr(($B=[3,ein0,zwei,drei,vier,fuenf,sech1,sieb2,acht,neun,zehn,elf,zwoelf,24=>vierundwanzig])[$n]?:$B[($r=$n-10)>9?$n%10:0].und.strtr($B[$r<10?$r:$n/10],[0,0,0,wei=>wan]).($r<10?zehn:zig),[$B[$n]?s:'',s,en,'3und'=>'','null',izi=>issi])
Explanation:
/* Three big irregularities: ein/eins, sech/sechs, sieb/sieben
and two small irregularities: vierundwanzig, .*dreissig
The major irregularities are represented by embedding numerals in the strings,
and as an implementation detail, we treat the input 0 a bit specially
Basic idea is performing string translations on the input with a unified code path.
*/
$B=[3,ein0,zwei,drei,vier,fuenf,sech1,sieb2,acht,neun,zehn,elf,zwoelf,24=>vierundwanzig];
$r=$n-10;
fn($n)=>
strtr( /* strtr#1 */
/* if $n is a key of $B, then the first argument of strtr#1 is $B[$n] */
$B[$n]?:
/* otherwise, compute: $x.und.$y.$z where
$x = $n < 20 ? '3' : $B[$n % 10]
$y = strtr($B[$n < 20 ? $n % 10 : $n / 10], ['1'=>'0','2'=>'0','wei'=>'wan'])
$z = $n < 20 ? 'zehn' : 'zig'
*/
$B[$r>9?$n%10:0].und
.strtr($B[$r<10?$r:$n/10],[0,0,0,wei=>wan])
.($r<10?zehn:zig)
,
/*
we replace:
'ein' => $n is a key of $B ? 'eins' : 'ein'
'1' => 's' (unless it was deleted in the $y computation)
'2' => 'en' (unless it was deleted in the $y computation)
'3' => 'null'
'3und' => '' (so that we don't get nullund.X.zig)
'izi' => 'issi' (for 'dreissig' case)
*/
[$B[$n]?s:'',s,en,'3und'=>'','null',izi=>issi]
)
Haskell, 341 bytes
n 0="null"
n 1="eins"
n 10="zehn"
n 11="elf"
n 2="zwei"
n 12="zwoelf"
n 20="zwanzig"
n 3="drei"
n 30="dreissig"
n 4="vier"
n 5="fuenf"
n 6="sechs"
n 16="sechzehn"
n 60="sechzig"
n 7="sieben"
n 17="siebzehn"
n 70="siebzig"
n 8="acht"
n 9="neun"
n t|t<20=n(t-10)++"zehn"
n t|mod t 10==0=n(div t 10)++"zig"
n t=n(mod t 10)++"und"++n(t-mod t 10)
05AB1E, 108 bytes
'¡Ö.•вß³á∍>jO5:+VÍÏnтågÏÇ&õL1δç´#₄’WH‹fÈ7Pm•#D9ÝÁè©ć«¦¦®¦¦¦.•8WÏ•š…zig«„iz…iss:.•j(}•3ô'z:®¦ć¨š…und«õšδì)˜Iè
Try it online or verify the entire list.
Explanation:
'¡Ö '# Push dictionary string "null"
.•вß³á∍>jO5:+VÍÏnтågÏÇ&õL1δç´#₄’WH‹fÈ7Pm•
# Push compressed string
# "eins zwei drei vier fuenf sechs sieben acht neun zehn elf zwoelf"
# # Split it on spaces to a list
D # Duplicate this list
9Ý # Push a list in the range [0,9]
Á # Rotate it to [9,0,1,2,3,4,5,6,7,8]
è # 0-based index each in the copy: ["zehn","eins","zwei","drei","vier",
# "fuenf","sechs","sieben","acht","neun"]
© # Store this list in variable `®` (without popping)
ć # Extract head; push first item and remainder-list separately
« # Append this "zehn" to each item in the remainder-list
¦¦ # Remove the first two items ("einszehn" and "zeizehn")
® # Push list `®` again
¦¦¦ # Remove the first three items ("zehn", "eins", and "zei")
.•8WÏ•š # Prepend "zwan" to the list
…zig« # Append "zig" to each item in the list
„iz…iss: # Replace all "iz" with "iss"
.•j(}• # Push compressed string "enzsz"
3ô # Split it into parts of size 3: ["enz","sz"]
'z: '# Replace both with "z"
# (with `®¦¦¦.•8WÏ•š…zig«„iz…iss:.•j(}•3ô'z:` we've pushed list
# ["zwanzig","dreissig","vierzig","fuenfzig","sechzig","siebzig",
# "achtzig","neunzig"])
®¦ # Push list `®`, and remove the first item "zehn"
ć # Extract head "eins"
¨ # Remove its last letter "s"
š # Prepend "ein" back to the list
…und« # Append "und" to each item in the list
õš # Prepend an empty string "" to the list
δ # Apply double-vectorized on the two lists:
ì # Prepend
) # Wrap everything on the stack into a list
˜ # Flatten it
Iè # Use the input to 0-based index into it
# (after which the result is output implicitly)
See this 05AB1E tip of mine (sections How to use the dictionary? and How to compress strings not part of the dictionary?) to understand why '¡Ö is "null"; .•вß³á∍>jO5:+VÍÏnтågÏÇ&õL1δç´#₄’WH‹fÈ7Pm• is "eins zwei drei vier fuenf sechs sieben acht neun zehn elf zwoelf"; .•8WÏ• is "zwan"; and .•j(}• is "enzsz".
JavaScript (ES6), 213 bytes
f=(n,m=8,u=n%10)=>n<20?`null ein5 zw26 drei vier fuenf sech7 sieb89 acht neun zehn elf zwoelf`.replace(/\d/g,n=>['enasisn'[(m^n)%10]]).split` `[~~n]||f(u,0)+'zehn':(u?f(u,2)+'und':'')+f(n/=10,0)+(n^3?'zig':'ssig')
Try it online! (test cases)
Try it online! (all values from 0 to 99)
How?
eins, zwei, sechs and sieben have different forms when they're used as prefixes. To support these alternate forms, some letters are encoded as decimal digits:
ein5 zw26 sech7 sieb89
Given a digit \$n\$ and a parameter \$m\$, the correct letter is obtained with:
[ 'enasisn'[(m ^ n) % 10] ]
// 0123456
The outer brackets are used to coerce the result to an empty string if it's undefined.
We use:
- \$m=0\$ for a prefix of
zehnorzig - \$m=2\$ for a prefix of
und - \$m=8\$ for the regular form
This gives the following table:
m | n | m^n | %10 | letter
---+---+-----+-----+-----------------
0 | 5 | 5 | 5 | 's' -> eins (1)
0 | 2 | 2 | 2 | 'a' \_ zwan (2)
0 | 6 | 6 | 6 | 'n' /
0 | 7 | 7 | 7 | - -> sech
0 | 8 | 8 | 8 | - \_ sieb
0 | 9 | 9 | 9 | - /
---+---+-----+-----+-----------------
2 | 5 | 7 | 7 | - -> ein
2 | 2 | 0 | 0 | 'e' \_ zwei
2 | 6 | 4 | 4 | 'i' /
2 | 7 | 5 | 5 | 's' -> sechs
2 | 8 | 10 | 0 | 'e' \_ sieben
2 | 9 | 11 | 1 | 'n' /
---+---+-----+-----+-----------------
8 | 5 | 13 | 3 | 's' -> eins
8 | 2 | 10 | 0 | 'e' \_ zwei
8 | 6 | 14 | 4 | 'i' /
8 | 7 | 15 | 5 | 's' -> sechs
8 | 8 | 0 | 0 | 'e' \_ sieben
8 | 9 | 1 | 1 | 'n' /
(1) Not used
(2) Used for -zig only
Retina 0.8.2, 162 157 bytes
12
zwo11
11
elf
(\d)(.)
$2und$1zig
3z
3ss
0?und1.+
zehn
0und
0
null
1
eins
su
u
2
zwei
eiz
anz
3
drei
4
vier
5
fuenf
6
sechs
7
sieben
8
acht
9
neun
sz|enz
z
Try it online! Link is to test suite that automatically generates the results for 0-99. Explanation:
12
zwo11
11
elf
Process 12 and 11 first.
(\d)(.)
$2und$1zig
For two digit inputs, switch the digits, insert und, and suffix zig.
3z
3ss
Except dreizig becomes dreissig.
0?und1.+
zehn
nullundeinzig becomes zehn, as does undeinzig when a suffix of any other digit.
0und
0
null
Delete a nullund prefix but a lone 0 becomes null.
1
eins
su
u
1 becomes eins unless it's a prefix in which case it's only ein.
2
zwei
eiz
anz
2 becomes zwei except before zig in which case it becomes zwan.
3
drei
4
vier
5
fuenf
6
sechs
7
sieben
8
acht
9
neun
Translate the remaining digits.
sz|enz
z
Fix up sechszehn, siebenzehn, sechszig and siebenzig to sechzehn, siebzehn, sechzig and siebzig.
Edit: Saved 4 bytes thanks to @Arnauld.
Mathematica, 427 bytes
Golfed version, try it online!
Module[{o,t},o={"null","eins","zwei","drei","vier","fuenf","sechs","sieben","acht","neun"};t={"","","zwanzig","dreissig","vierzig","fuenfzig","sechzig","siebzig","achtzig","neunzig"};Which[0<=n<10,o[[n+1]],10<=n<20,StringJoin[{"zehn","elf","zwoelf","dreizehn","vierzehn","fuenfzehn","sechzehn","siebzehn","achtzehn","neunzehn"}[[n-9]]],20<=n<100,StringJoin[{If[Mod[n,10]!=0,o[[Mod[n,10]+1]]<>"und",""],t[[Quotient[n,10]+1]]}]]]
Ungolfed version
germanNumber[n_Integer] := Module[{ones, tens},
ones = {"null", "eins", "zwei", "drei", "vier", "fuenf", "sechs", "sieben", "acht", "neun"};
tens = {"", "", "zwanzig", "dreissig", "vierzig", "fuenfzig", "sechzig", "siebzig", "achtzig", "neunzig"};
Which[
0 <= n < 10, ones[[n + 1]],
10 <= n < 20, StringJoin[{"zehn", "elf", "zwoelf", "dreizehn", "vierzehn", "fuenfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn"}[[n - 9]]],
20 <= n < 100, StringJoin[{If[Mod[n, 10] != 0, ones[[Mod[n, 10] + 1]] <> "und", ""], tens[[Quotient[n, 10] + 1]]}]
]
]
Python + num2words, 54 107 104 91 bytes
lambda n:num2words(n,0,'de').translate({252:'ue',246:'oe',223:'ss'})
from num2words import*
+53 bytes because we have to replace non-ASCII with ASCII equivalents.
-3 thanks to @CreativeName
-13 thanks to @Neil
Charcoal, 127 bytes
≔E⪪”&⌈⪪γ,‴\Π↶QBσPc�⁸QDι,ÀH⁸²V≧τ⁼θCUH↶⊙↨H⊖O³j⪫JWX<⸿Dη″β↓”p⪪ιqζNθ≔﹪θχη¿‹θ¹³⁺⌈§ζθ×s⁼θ¹¿‹θ²⁰⁺⌊§ζη⌈§ζχ«∧η⁺⌈§ζηund⌊§ζ÷θχ⎇⁼³÷θχss¦z¦ig
Try it online! Link is to verbose version of code. Explanation:
≔E⪪”...”p⪪ιqζ
Split a compressed string on p and then split each substring on q (for the numbers 2, 6 and 7).
Nθ≔﹪θχη
Input the number in base 10 and take the remainder modulo 10.
¿‹θ¹³
If the number is less than 13, then...
⁺⌈§ζθ×s⁼θ¹
... output the maximum of the two substrings for that number, plus an extra s if the number is 1.
¿‹θ²⁰
Otherwise, if the number is less than 20, then...
⁺⌊§ζη⌈§ζχ
... output the minimum of the two substrings for that number modulo 10, plus the substring for 10 (although I could have just used the string literal at this point).
«
Otherwise:
∧η⁺⌈§ζηund
If the remainder modulo 10 is not zero, then output the maximum of the two substrings for that number, plus the literal string und.
⌊§ζ÷θχ
Output the minimum of the two substrings for the number integer divided by 10. This outputs zwan for 20-29, sech for 60-69, and sieb for 70-79.
⎇⁼³÷θχss¦z¦ig
Output ssig if the number is in the range 30-39 otherwise output zig.
It looks as if the alternate forms are just the first four letters of the regular form, but this only works if you use the original accented characters, which Charcoal can't compress anyway, and working around the cases of 15 and 50-59 just takes too many bytes.