Your task

Given a numerical string or integer \$\ge 0\$ (which may have leading zeros), convert it to letters using the below rules.


Loop through the digits:

  • If the digit is 0 or 1, combine it with the next digit and output that letter of the alphabet (0-indexed).
  • If the digit is 2, combine with the next digit ONLY IF the next digit is between 0 and 5 (inclusive). Otherwise, output c (2nd letter of the alphabet, 0-indexed).
  • If the digit is 3 or more, output that letter of the alphabet (0-indexed).


Our input is the number 12321.

We loop through the digits:

  • 1: this is less than 2, so we keep this and wait for the next digit.
  • 2: combine with the previous digit, 1, to get 12. Index into the lowercase alphabet (0-indexed) to get m
  • 3: this is more than 2, so we output the 3rd letter of the alphabet (0-indexed), d
  • 2: this is 2, so we check the next digit. It is less than 6, so we wait.
  • 1: combine with the previous digit, 2, to get 21. Index into the lowercase alphabet (0-indexed) to get v

Our output is mdv.

Test cases


Input   Output
132918  ncjs
79411   hjel
695132  gjfnc
800125  iamf
530987  fdjih
144848  oeiei
93185   jdsf
922846  jwieg
187076  shhg
647325  gehdz


Input  Output
0      a
1      b
25     z
26     cg
000    aa
123    md
0123   bx
1230   mda
12310  mdk
12345  mdef
00012  abc

Feel free to create your own test cases in this TIO of some ungolfed Python code.


  • Everything must be 0-indexed
  • You cannot ignore leading 0s (see the 5th and 6th edge-cases)
  • If the last digit is 0, 1, or 2, and it has not already been used by the previous digit, output "a", "b", or "c" respectively (see edge-cases)
  • This is , so shortest answer in bytes wins.
  • \$\begingroup\$ Sandbox \$\endgroup\$
    – The Thonnu
    Nov 20, 2022 at 13:47
  • 3
    \$\begingroup\$ It's worth updating the spec to directly indicate that a trailing 0, 1 or 2 with nothing following it should output a, b or c (following the rule for 3 or more). Clear (I think) from the 'edge cases', but better to spell it out. \$\endgroup\$ Nov 20, 2022 at 14:09
  • \$\begingroup\$ @DominicvanEssen thanks. I've updated the challenge. \$\endgroup\$
    – The Thonnu
    Nov 20, 2022 at 15:23
  • 1
    \$\begingroup\$ The following challenge should be: given any strings that contain only lower asciis, output the corresponding string with fewest numbers. \$\endgroup\$
    – tsh
    Nov 21, 2022 at 7:59
  • \$\begingroup\$ Many languanges (including node even though there's a node answer below) discard leading zeroes when reading integers. Those languages would need to read strings instead. \$\endgroup\$
    – Sandra
    Nov 21, 2022 at 10:08

17 Answers 17


JavaScript (Node.js), 49 bytes

//                           we match all patterns consisting of either:
//              +----------> a '2' followed by '0' to '5', or
//              |      +---> any digit optionally preceded by '0' or '1'
//             _|__   _|__
//            /    \ /    \
//                               \_____________/
//                                      |
// and replace each of them with a <----+
// letter in lower case

Try it online!


Python 3.8 (pre-release), 58 bytes

f=lambda s:s and chr(97+int(s[:(I:=1+(s<"26"))]))+f(s[I:])

Try it online!

This is based on the observation that we can simply compare the entire leftover string to "26" to decide whether to consume one or two characters.


><> (Fish), 83 bytes

^ o+'a'+-'0'i*a<


enter image description here

Top row is basic reading lines and exiting at the end. Red part at the end is the single digit case.

Middle row is the 0 or 1 case.

Bottom row 2 rows are the 2 case. Top branch is C, bottom is 2 digit number.


Retina 0.8.2, 32 bytes


Try it online! Link includes test cases. Explanation:


Use @Arnauld's regex to identify the numbers from 0 to 25 inclusive.


Replace each number with a string of that many #s followed by an a.


Repeatedly cycle letters though the alphabet, decreasing the number of preceding #s each time, until there are no #s left.


05AB1E, 17 bytes


Output as a list of characters.

Try it online or verify all test cases.


.œ                # Get all possible partitions of the (implicit) input
  ʒ         }     # Filter this list by:
   €       P      #  All parts in a partition
    g             #  Should have a length
     3‹           #  Less than 3
       y  «       #  And in addition should also all be
        ₂‹        #  Less than 26
            }θ    # After the filter: leave the last valid partition
              Asè # Index each inner index into the lowercase alphabet
                  # (after which this character-list is output implicitly as result)
  • \$\begingroup\$ So close: you got beaten by half a byte by the Nibbles answer. \$\endgroup\$
    – The Thonnu
    Nov 24, 2022 at 16:33

Ruby, 49 bytes

->s{eval ['""',*s.scan(/2[0-5]|[01]?./)]*"<<97+"}

Try it online!

  • \$\begingroup\$ Clever! Nice work. \$\endgroup\$
    – Jordan
    Nov 22, 2022 at 1:03

Excel (ms365), 169 bytes


The idea here is to use REDUCE() to iterate n-times (length of input) over this input and while doing so keep a shadow-record in the element next to given input with the alphabetic output while we check all numbers in order from left to right.

I do have a feeling this could be shortened though.

enter image description here

  • \$\begingroup\$ Having a bit of trouble reproducing your output in MS365 Excel V.2210 - output of B1 is equivalent to ={"","nnnnnn"} \$\endgroup\$ Nov 26, 2022 at 20:09
  • \$\begingroup\$ Do you happen to have a different locale language setting? It may affect the way an array is written in CHOOSE...otherwise I wouldn't know as it works for me. @TaylorAlexRaine \$\endgroup\$
    – JvdV
    Nov 26, 2022 at 20:32
  • \$\begingroup\$ I tried a couple of different language settings including South Africa, US, and UK, and had no luck getting it to work - I don't doubt that your solution is working, but I suggest that you indicate version and language as Excel can be extremely picky about these settings \$\endgroup\$ Nov 26, 2022 at 20:41
  • \$\begingroup\$ I'm going to have a look into that sometime soon when behind a pc 👍 \$\endgroup\$
    – JvdV
    Nov 26, 2022 at 20:53
  • 1
    \$\begingroup\$ @TaylorAlexRaine, right so I could reproduce your issue. As mentioned before you must be aware that arrays are written differently in different locale settings. The culprit is =CHOOSE({1,2} which in your locale settings may probably be written as =CHOOSE({1\2}. \$\endgroup\$
    – JvdV
    Nov 28, 2022 at 11:21

K (ngn/k), 42 38 bytes

-4 bytes thanks to @coltim

Uses loopy walt's recursive approach. There is probably some clever way to rewrite it using a fold.

It takes the length 2 prefix and compare it with 26 to decide if we convert 1 or 2 characters. After the conversion, it appends a recursive call on the remaining string.


Try it online!

  • 1
    \$\begingroup\$ You can trim one byte by using $[x;...] instead of $[#x;...] (empty lists are false-y, non-empty lists are truthy), and another byte by using 2$x in place of x@!2 \$\endgroup\$
    – coltim
    Nov 25, 2022 at 21:55
  • 1
    \$\begingroup\$ Similarly, n$x instead of n#p can save a pair of bytes (I would have edited the above, but I was too slow. oops) \$\endgroup\$
    – coltim
    Nov 25, 2022 at 22:00
  • \$\begingroup\$ thanks! i had completely forgotten the n$string overload \$\endgroup\$
    – Traws
    Nov 25, 2022 at 22:20

Nibbles, 17 16.5 bytes (33 nibbles)

``;                                 # launch recursive function:
   $                                # starting with input list of digits,
    $                               # stopping when argument is empty
     $                              #   and then returning the empty argument,
                                    # otherwise:
                `:"26"$             #   make a list of "26" and the argument string,
              `<                    #   sort this list of strings,
             \                      #   reverse it,
            ?          $            #   get the index of the argument in this,
           ;                        #   and save this as the number of elements
                                    #   to take from the argument (1 or 2);
          <                         #   now, take that many elements
        `r                          #   read this as an integer,
       +                "a"         #   add this to "a",
      :                             #   and append onto this
                           _        #   a recursive call with argument:
                            >$      #     drop the saved number of elements
                              @     #     from the current argument

enter image description here See some more test & edge cases


Jelly, 20 bytes


Try it online! Or see the test-suite.


Charcoal, 24 bytes


Try it online! Link is to verbose version of code. Explanation: Port of @loopywalt's Python solution.


Loop until the input is empty.


Determine how many digits to extract.


Output the appropriate letter.


Slice off those digits.


C (clang), 85 75 bytes

  • -7 thanks to jdt
  • -3 thanks to ceilingcat

Takes strings to preserve leading zeroes.


Try it online!


C (clang), 64++ bytes


Try it online!

If the output not being null-terminated is a problem, it's 1 extra byte to write to stdout:


Try it online!


sclin, 32 bytes

"2[0-5]|[01]?."\; /#
"&":97+ c>S

Try it here! Port of @Arnauld's answer.

For testing purposes:

["132918""79411""695132""800125""530987""144848""93185""922846""187076""647325""0""1""25""26""123""0123""1230""12310""12345"] \>S map ; n>< n>o
"2[0-5]|[01]?."\; /#
"&":97+ c>S

jq -Rr, 43 bytes


Try it online!


><>, 54 50 bytes

<v+*a  ~!

Try it online!


Get all the input, mod each by 12 to get numbers and reorder it in the input order.


Then discard the end-of-input-byte.

If we call the top of the stack x and the 2nd top value of the stack y then:

If (x < 2 or (x == 2 and y < 6)) and len(stack) > 1

<v+*a ~!
Then x = x * 10 + y

Print x + 97 as a character.


Python 3, 73 71 bytes

lambda s:re.sub('2[0-5]|[01]?.',lambda c:chr(97+int(c[0])),s)
import re

Try it online!

Uses Arnauld's Regex expression.

Python 3, 117 bytes

f=lambda n:n and(chr(97+int(n[:2]))+f(n[2:])if n[0]in'01'or n[0]=='2'and int(n[1])<6 else chr(97+int(n[0]))+f(n[1:]))

Try it online!

Recursive lambda function.

Python 3, 135 bytes

def f(n):
 if n=='':return''
 if d in'01'or d=='2'and int(n[1])<6:a,b=n[:2],n[2:]
 return chr(97+int(a))+f(b)

Try it online!

Recursive def function.

Python 3, 212 bytes

def f(n,i=0,o='',c=lambda s:chr(97+int(s))):
 while i<len(n)-1:
  if d in'01':i+=1;o+=c(d+n[i])
  elif d=='2'and int(n[i+1])<6:i+=1;o+=c(d+n[i])
 if i<len(n):o+=c(n[i])
 return o

Try it online!

Iterative def function.

  • 7
    \$\begingroup\$ Answering your own posting right away is unfair (you've had more time to work on it). Would have been much better to put a link in your OP to one of these as example code. \$\endgroup\$
    – Noodle9
    Nov 20, 2022 at 20:10
  • 1
    \$\begingroup\$ As a note, it's better to wait a few days before doing so (usually if your post gets lots of answers), otherwise wait a little longer (max a week) \$\endgroup\$
    – DialFrost
    Nov 20, 2022 at 23:15
  • 2
    \$\begingroup\$ In TheThonnu's defence: this doesn't really look like it was meant as a competing entry to me. \$\endgroup\$
    – loopy walt
    Nov 21, 2022 at 21:27

Your Answer

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

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