Remembering numbers can be difficult. Remembering a word may be easier. In order to memorize big numbers, I created a way to pronounce them in a leetspeak-like way.


Each digit is first replaced by its corresponding letter:

0 => O
1 => I
2 => R
3 => E
4 => A
5 => S
6 => G
7 => T
8 => B
9 => P

After the replacement, two additional things are done to improved pronunciation:

  • Between two consonants, a U is added.

  • Between two vowels, a N is added.

Examples/test cases

512431 => SIRANENI
834677081 => BENAGUTUTOBI
6164817 => GIGABIT

What's impossible

  • Letters and numbers mixed in the same word
  • Two successive consonants or two successive vowels
  • Letters that are not in the list above
  • Other characters


The goal of this is to create a 2-way translator for this concept.

  • Your program must first understand by itself if it's letter-to-number or number-to-letter translation.
  • It must check for the entry to be properly formed.
  • If everything is correct, display the translation.
  • Else, display an error message, nothing, return a falsey value or crash the program.


  • The input number/string can be entered in whatever format you want (stdin, argument, ...)
  • This is , so shortest answer wins.
  • Standard loopholes are forbidden.
  • 11
    \$\begingroup\$ Shouldn't GIGATESTER be GIGATESUTER? \$\endgroup\$
    – kamoroso94
    Jun 2, 2017 at 10:35
  • 6
    \$\begingroup\$ Usually, we don't ask for the entry to be 'properly formed', as it's just extra code for no good reason (see the meta question for good challenge recommendations). Additionally, what does 'properly formed' mean? \$\endgroup\$
    – Okx
    Jun 2, 2017 at 10:39
  • 11
    \$\begingroup\$ If input validation is required, you should include a decent batch of test cases of invalid inputs (especially letter-only inputs that are almost valid). I expect that this will actually be the main part of the challenge in many languages. \$\endgroup\$ Jun 2, 2017 at 10:44
  • 2
    \$\begingroup\$ I agree with @MartinEnder that you should add some test cases for invalid cases, like AB23; AEI; BB; Z; ACE; giga; !@#$; -123; etc. Also, based on the validation rules, we can convert 6164735732 => GIGATESTER, but GIGATESTER will result in a false value, because of ST (two successive consonants rule). The way your challenge is currently set up, the main part of the challenge is the validation, instead of the conversion. I'm fine with that, but the validation should be defined a bit better in that case. \$\endgroup\$ Jun 2, 2017 at 14:59
  • 2
    \$\begingroup\$ Your program must first undersand by itself if it's letter-to-number or number-to-letter translation. So the translation has to be two-way? The preceding text and the test cases suggest only number to letter \$\endgroup\$
    – Luis Mendo
    Jun 2, 2017 at 15:21

17 Answers 17


JavaScript (ES6), 130 bytes

Takes input as a string in both translation ways. Returns either the translation as a string or false in case of an invalid input.




console.log(f("512431"))          // SIRANENI
console.log(f("834677081"))       // BENAGUTUTOBI
console.log(f("3141592"))         // ENINANISUPUR
console.log(f("1234567890"))      // IRENASUGUTUBUPO
console.log(f("6164735732"))      // GIGATESUTER

console.log(f("SIRANENI"))        // 512431 
console.log(f("BENAGUTUTOBI"))    // 834677081
console.log(f("ENINANISUPUR"))    // 3141592
console.log(f("IRENASUGUTUBUPO")) // 1234567890
console.log(f("GIGATESUTER"))     // 6164735732

console.log(f("AB23"))            // false
console.log(f("AEI"))             // false
console.log(f("ZZ"))              // false

  • \$\begingroup\$ If it doesn't work perfectly, don't post it. \$\endgroup\$
    – Okx
    Jun 2, 2017 at 12:30
  • \$\begingroup\$ Hopefully now working as expected. \$\endgroup\$
    – Arnauld
    Jun 2, 2017 at 12:55
  • \$\begingroup\$ ... or crash when there are reserved regexp characters in input. Still valid though \$\endgroup\$
    – edc65
    Jun 7, 2017 at 9:25

Japt, 61 59 92 85 84 bytes

I'm offline for most of the (long) weekend, if any more issues are discovered with this, please ask a mod to delete it for me until such a time as I can fix it.

Takes input as a string for both operations and returns a string for both as well or false for invalid input.Assumes number inputs will always contain multiple digits, add 1 byte replacing with Un<space> if that's not valid. Returns false for test case GIGATESTER but, according to the rules, that should be invalid input.

V="OIREASGTBP"UÉ?¡VgXÃe"%v"²_i1'NÃe"%V"²_i1'UÃ:!Uè"%v%v|%V%V|[^{V}NU]" ©Ur"N|U" £VaX

Try it: Numbers -> Letters or Letters -> Numbers

  • 2 4 bytes saved thank to obarakon, who also convinced me to take this up again after I abandoned it earlier. I wish he hadn't!
  • 33 26 25(!) bytes sacrificed implementing a quick fix (i.e., yet to be fully golfed) to check input validity.


(Yet to be updated to the latest version)

                          :Implicit input of string U.
V="..."                   :Assign the string of letters to variable V, in order.
UÉ                        :Subtract 1 from U, which will give a number (truthy) if the input is a number or NaN (falsey) if the input is a string.
?                         :If it's a number then
¡                         :    Map over the input string, replacing each character (digit) with ...
VgX                       :      the character in string V at index X, the current digit.
à                        :    End mapping.
e                         :    Recursively replace ...
"%v"²                     :      every occurrence of 2 vowels (RegEx) ...
_i1'N                     :      with the current match with an "N" inserted at index 1.
à                        :    End replacement.
e                         :    Another recursive replacement of ...
"%V"²                     :      every occurrence of 2 non-vowel characters (i.e., consonants) ...
_i1'U                     :      with the current match with a "U" inserted at index 1.
à                        :    End replacement.
:                         :Else, if it's a string then
Uè"%v%v|%V%V|[^{V}NU]"    :    Count the number of matches of 2 successive vowels OR 2 successive non-vowels OR any character not in contained in string V plus N & U.
                          :    (The longest part of this code is the fecking input validation!)
?                         :    If that count is greater than 0 then
T                         :       Return 0.
:                              Else
Ur"N|U"                   :        Replace every occurrence of "N" OR "U" in string U with nothing.
£                         :        Map over the string, replacing each character (letter) with ...
VaX                       :         the index of the current character X in string V.
                          :Implicit output of resulting string
  • \$\begingroup\$ Doesn't seem to handle invalid input such as AEI \$\endgroup\$
    – Emigna
    Jun 2, 2017 at 15:55
  • \$\begingroup\$ @Emigna: Ah, goddamnit! You'd imagine, after getting burned by the first "Rule" initially, we would have thought to read the rest of the rules! :\ I took the "What's impossible" section to imply we wouldn't have to handle any of those points.Fix coming shortly. \$\endgroup\$
    – Shaggy
    Jun 2, 2017 at 16:00

Python 3, 147 bytes

lambda c:c in"0134"
def f(n):
 for i in n:
  if a==b:o+="UN"[a]

Try it online!


Java (OpenJDK 8), 416 410 399 382 376 370 bytes

-2 bytes thanks to @Cyoce

-17 more bytes thanks to an idea by @Cyoce

-6 bytes thanks to @KevinCruijssen

s->{String c="[RSGTBP]",v="[OIEA]",o="([256789])",e="([0134])";boolean b=s.matches("(c$|v$|(c|vN)(?=v)|(cU|v)(?=c))+".replace("c",c).replace("v",v));int i=-1;for(s=b?s.replaceAll("[UN]",""):s.matches("[0-9]+")?s.replaceAll(e+"(?="+e+")","$1N").replaceAll(o+"(?="+o+")","$1U"):i/0+"";i<9;s=b?s.replace(v,c):s.replace(c,v)){c=++i+"";v="OIREASGTBP".charAt(i)+"";}return s;}

Try it online!

Ugh, java replacement is so verbose.

Function which takes a string and returns the string translated from number -> letter or vice versa. Crashes on invalid input (you can see this in the tio example where it outputs the correct values for the first 10 test cases and then crashes with a divide by zero error which shows in the debug view)

Ungolfed (the first and last term of the for loop are pulled out for readability)

s-> {
    String c="[RSGTBP]", v="[OIEA]", o="([256789])", e="([0134])"; 
    boolean b=s.matches("(c$|v$|(c|vN)(?=v)|(cU|v)(?=c))+".replace("c",c).replace("v",v)); // lovely regex, explained below
    int i=-1;
    s= b? 
        s.replaceAll("[UN]",""); // remove N's and U's
        s.replaceAll(e+"(?="+e+")","$1N").replaceAll(o+"(?="+o+")","$1U"); // add N's and U's for separating vowels and consonants
        :i/0+""; // throw an error, looks like a sting for the ternary
    for(;i<9;) { 
        s=b?s.replace(v,c):s.replace(c,v); // if it started with numbers, go to letters, or vice versa
    return s;

The regex for matching the numbers is simple, but here is the regex for matching the letters to numbers case

(                             )+   every part of the word is
 c$                                a consonant at the end of the word
   |v$                             or a vowel at the end of the word
      |(c|vN)(?=v)                 or a consonant or a vowel + N followed by a vowel
                  |(cU|v)(?=c)     or a consonant + U or a vowel followed by a consonant

with c = [RSGTBP] and v = [OIEA]
  • \$\begingroup\$ Not that it significantly improves your massive byte count, but you can remove the parentheses around (s)-> \$\endgroup\$
    – Cyoce
    Jun 2, 2017 at 17:29
  • \$\begingroup\$ @Cyoce every byte helps \$\endgroup\$
    – PunPun1000
    Jun 2, 2017 at 17:38
  • \$\begingroup\$ since all your branches of the if statement are assignments (which return a value), try replacing the if...else if...else with the conditional operator ?:, prefacing it with Object _= to make it a valid statement. Not sure if this would actually help the byte count, but I think it will. \$\endgroup\$
    – Cyoce
    Jun 2, 2017 at 17:57
  • \$\begingroup\$ Two small things you can golf. You can remove the String t, because you only use it once. So t.charAt(i)+"" becomes "OIREASGTBP".charAt(i)+"" (-4 bytes). And you can place the last line inside the for-loop after the i<9; inside the for-loop declaration. So it becomes for(;i<9;s=b?s.replace(v,c):s.replace(c,v)){ (-1 byte). Oh, and you can put the s=b?... that comes right after the int i=-1; inside the for-loop as well: for(s=b?...;i<9;... (-1 byte). \$\endgroup\$ Jun 6, 2017 at 6:51

sed, 123 bytes



First, we surround digits with _ (for vowels) or = (for consonants).

If we didn't make any substitutions, we are converting letters to digits, so it's a simple substitution, and delete U and N. Then quit.

Otherwise, we branch to label a, where we deal with consecutive vowels and then consecutive consonants. Then transform digits to letters, and delete the marker characters we introduced in the first step.


PHP; 129 127 267 259 228 bytes


Run as pipe with -nR or try it online.


# if not digits, translate letters to digits and remember original
# translate digits to letters:
for($r=$c=($t=strtr($s,$n,$l))                      # result = first letter
    [$i++];$d=$t[$i++];)                            # loop through letters
    $r.=((trim($c,AEIO)xor$x=trim($d,AEIO))?"":UN[!$x]) # append delimiter if needed
        .$c=$d;                                         # append next letter
    $o              # if original was remembered,
        ?$o==$r         # compare original to final result
            ?$s         # if equal, print digits
            :X          # else print X (as error message)
        :$r;        # else print letters

Java 8, 312 308 304 301 294 290 bytes

s->{String r="",x="([AEIOU])",y="([BGNPRST])",z="0O1I2R3E4A5S6G7T8B9P";for(int c:s.getBytes())r+=c!=78&c!=85?z.charAt((c=z.indexOf(c)+(c<58?1:-1))<0?0:c):"";return s.matches("(("+x+y+")*"+x+"?)|(("+y+x+")*"+y+"?)|\\d*")?r.replaceAll(x+"(?="+x+")","$1N").replaceAll(y+"(?="+y+")","$1U"):"";}

-4 bytes (308 → 304) for a bug-fix (doesn't happen often that the byte-count decreases when I fix a bug in my code.. :D)

EDIT: Uses a different approach than @PunPun1000's Java answer by first creating the return-String in a for-loop over the characters, and then uses a more abstract regex to validate it in the return-ternary (the input is either all digits, or are the given vowels and consonants alternating (so without any adjacent vowels nor consonants).


Try it here.

s->{                                   // Method with String parameter and String return-type
  String r="",                         //  Result-String
    x="([AEIOU])",y="([BGNPRST])",     //  Two temp Strings for the validation-regex
    z="0O1I2R3E4A5S6G7T8B9P";          //  And a temp-String for the mapping
  for(int c:s.getBytes())              //  Loop over the characters of the input-String
    r+=                                //   Append to the result-String:
       c!=78&c!=85?                    //    If the character is not 'N' nor 'U':
        z.charAt(                      //     Get the character from the temp-String `z`
         (c=z.indexOf(c)+              //      by getting the character before or after the current character
            +(c<58?1:-1))              //      based on whether it's a digit or not
             <0?0:c)                   //      and a 0-check to prevent errors on incorrect input like '!@#'
       :                               //    Else:
        "";                            //     Append nothing
                                       //  End of loop (implicit / single-line body)
  return s.matches("(("+x+y+")*"+x+"?)|(("+y+x+")*"+y+"?)|\\d*")?
                                       //  If the input is valid
                                       //  (Only containing the vowels and consonants of `x` and `y`, without any adjacent ones. Or only containing digits)
    r                                  //   Return the result
     .replaceAll(x+"(?="+x+")","$1N")  //    after we've added 'N's if necessary
     .replaceAll(y+"(?="+y+")","$1U")  //    and 'U's if necessary
   :"";                                //  Or return an Empty String if invalid
}                                      // End of method

The validation regex:


Perl 5 -p, 155 bytes

$v="AEIO]";$c="RSGTBP]";$,=OIREASGTBP;eval"/[^0-9$,UN]|[^$v N|N[^$v|[^$c U|U[^$c|^[NU]|[NU]\$/x||y/0-9$,UN/$,0-9/d+s/[$v\\K(?=[$v)/N/g+s/[$c\\K(?=[$c)/U/g"

Try it online!

Recognizes some invalid inputs that the other Perl entry does not. Outputs the string unchanged if it is not valid.


05AB1E, 76 bytes

.•.Ÿ^91Ý•Ul„nuSKDXSKg0QVvXyk}JYP©0Êi®}¤«SXuèŒ2ùv…U NSy0èìžMuyåOè})Jᨹd_iQ®P

Try it online!

Returns the falsy value 0 for invalid input.


Bash, 241 238 235 bytes

q=OIREASGTBP;[[ $1 == +([0-9]) ]]&&(x=`tr 0-9 $q<<<$1`;m={B,G,P,R,S,T};n={A,E,I,O};for i in `eval echo $m$m$n$n`;{ a=${i::1};b=${i:1:1};x=${x//$a$b/$a'U'$b};a=${i:2:1};b=${i:3:1};x=${x//$a$b/$a'N'$b};};echo $x)||tr $q 0-9<<<$1|tr -d UN

Try it online!

Less golfed:

q=OIREASGTBP;                          save string in q
[[ $1 == +([0-9]) ]]&&(                if argument 1 is only digits
x=`tr 0-9 $q<<<$1`;                    save in x each digit translated to corresponding letter
for i in `eval echo $m$m$n$n`;{        generates all combinations of vowels and consonants
                                       BBAA BBAE ... TTOI TTOO
   a=${i::1};                          saves first consonant in a
   b=${i:1:1};                         saves second consonant in b
   x=${x//$a$b/$a'U'$b};               insets U between consonants
   a=${i:2:1};                         saves first vowel in a
   b=${i:3:1};                         saves second vowel in b
   x=${x//$a$b/$a'N'$b};               inserts N between vowels
echo $x                               echoes result
)||                                   if argument contains letters
  tr $q 0-9<<<$1|tr -d UN             translates letter to corresponding number and deletes U and N

PHP, 268 Bytes


Try it online!


Perl, 127 bytes

126 bytes + 1 byte command line



 echo -n "512431" | perl -p entry.pl

Should follow all the challenge rules - can accept letters or numbers and will error (division by zero) if validation fails

  • \$\begingroup\$ Validation has false positives on input NO and US. \$\endgroup\$
    – Value Ink
    Jun 3, 2017 at 1:05

Ruby, 205+1 = 206 bytes

Uses the -p flag for +1 byte. Now with an exhaustive input validation system.

d,w=%w"0-9 OIREASGTBP"
gsub /([#{a='AEIO])(?=\g<1>)'}/,'\0N'
gsub /([^#{a}/,'\0U'):(+~/^(([AEIO])(N(?=[AEIO])|(?=\g<4>)|$)|([RSGTBP])(U(?=\g<4>)|(?=\g<2>|$)))+$/;$_.tr!("NU","").tr!w,d)

Try it online!

  • \$\begingroup\$ This doesn't seem to convert letters to numbers, nor does it do any validation! \$\endgroup\$
    – Jarmex
    Jun 3, 2017 at 0:03
  • \$\begingroup\$ @Jarmex oops, added validation! It's a huge validation check but I don't have much choice in the matter \$\endgroup\$
    – Value Ink
    Jun 3, 2017 at 0:58

Python 3, 741 bytes

from functools import reduce;c=0;e="";n="NU";v="AEIOU";l="OIREASGTBPNU";x=('0','O'),('1','I'),('2','R'),('3','E'),('4','A'),('5','S'),('6','G'),('7','T'),('8','B'),('9','P');s=input()
    int(s);y=reduce(lambda a,kv:a.replace(*kv),x,s)
    for i in y:
        if i in v:
                if y[c+1] in v:e+="N"
                if not y[c+1] in v:e+="U"
    for i in s:
        if not i in l:
    y=reduce(lambda a,kv:a.replace(*kv[::-1]),x,s)
    for i in y: 
        if not i in n:e+=i

Try it online!

There's a lot of room for improvement, I know.


Python3, 246 bytes

v=lambda x:x in"AEIO"
print(r(lambda x,y:x+(("U",""),("","N"))[v(x[-1])][v(y)]+y,map(lambda x:V[x],map(int,i)))if i.isdigit()else r(lambda x,y:x*10+V.index(y),filter(lambda x:x in V,i),0))    


  • map input to a list of int
  • map list of int to their position in the alphabet
  • reduce list by adding accumulator, plus an element of a dicttuple, plus the current element
    • the dicttuple is a truth table based on two elements, being vowel or not

JavaScript (ES6), 120

A function taking input as a string. It returns the properly translated string if the input is valid, else false or the function crashes.


Less golfed

n => 
  var t = n => { // function to translate, no check for invalid input
    var v = 2; // 1 = digit map to vowel, 0 = digit map to consonant, start with 2
    var z = 'OIREASGTBP'; // digits mapping
    return n.replace(/./g,
      d => 1/d // digit / alpha check
        ? ( // if digit
            w = v, // save previous value of v
            v = d < 5 & d != 2, // check if current digit will map to wovel or consonant
            (w != v 
             ? '' // if different - wovel+consonant or consonant+wovel or start of input
             : 'UN'[v] // if equal, insert required separator
            ) + z[d] // add digit translation
        : ( // if alpha
             a = z.search(d), // look for original digit. Could crash if d is a reserved regexp char (not valid input)
             a != -1 ? a : '' // if digit found add to output, else do nothing
  var q = t(n); // translate input an put in q
  if (t(q) == n) // translate again, result must be == to original input
    return q; // if ok return result
    return false; // else return false


var F=

;`512431 => SIRANENI
834677081 => BENAGUTUTOBI
6164817 => GIGABIT`
.forEach(x => {
  var [a,b] = x.match(/\w+/g)
  var ta = F(a)
  var tb = F(b)
  console.log(a==tb ? 'OK':'KO', a + ' => '+ ta)
  console.log(b==ta ? 'OK':'KO', b + ' => '+ tb)

function go() {
  O.textContent = F(I.value)

<input id=I value='NUNS' oninput='go()'>
<pre id=O></pre>


APL (Dyalog Unicode), 71 bytes

' '~⍨,({' ',2{∧/x←'AEIOU'∊⍨⍺⍵:'N'⋄∧/~x:'U'⋄' '}/⍵},⍪)'OIREASGTBP'[⍎¨⍕⎕]

Try it online!

This turned out surprisingly short!

Uses ⎕IO←0 (0-indexing).


' '~⍨,({' ',2{∧/x←'AEIOU'∊⍨⍺⍵:'N'⋄∧/~x:'U'⋄' '}/⍵},⍪)'OIREASGTBP'[⍎¨⍕⎕]
                                                                  ⍎¨⍕⎕  get the digits of the input
                                                     'OIREASGTBP'[    ] map to the correct letters
      {' ',2{∧/x←'AEIOU'∊⍨⍺⍵:'N'⋄∧/~x:'U'⋄' '}/⍵},⍪                     join the following with the tabled argument:
                                              /⍵                        map pairs of the argument to:
           2 ∧/x←'AEIOU'∊⍨⍺⍵:'N'                                        N if vowel pair,
                                ⋄∧/~x:'U'                               U if consonant pair,
                                         ⋄' '                           space otheriwse.
       ' ',                                                             prepend a space
    ,                                                                   flatten the result
' '~⍨                                                                   remove all spaces

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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