# Number to letters

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

### 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).

### Example

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

#### Random

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


#### Edge-cases

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.

### Clarifications

• 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.
• Sandbox Nov 20, 2022 at 13:47
• 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. Nov 20, 2022 at 14:09
• @DominicvanEssen thanks. I've updated the challenge. Nov 20, 2022 at 15:23
• The following challenge should be: given any strings that contain only lower asciis, output the corresponding string with fewest numbers.
– tsh
Nov 21, 2022 at 7:59

# 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'
//             _|__   _|__
//            /    \ /    \
s=>s.replace(/2[0-5]|?./g,n=>Buffer([+n+97]))
//                               \_____________/
//                                      |
// 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

>i:0(?;'0'-:2(?v:2=?v'a'+o
^ o+'a'+-'0'i*a<
+'a'~$o'c'v?(6:-'0'i<.01o 1o+'a'+*a$<.0 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

2[0-5]|?.
$*#a +T##l_l#\w  Try it online! Link includes test cases. Explanation: 2[0-5]|?.  Use @Arnauld's regex to identify the numbers from 0 to 25 inclusive. $*#a


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

+T##l_l#\w


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

# 05AB1E, 17 bytes

.œʒ€g3‹y₂‹«P}θAsè


Output as a list of characters.

Explanation:

.œ                # 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)

• So close: you got beaten by half a byte by the Nibbles answer. Nov 24, 2022 at 16:33

# Ruby, 49 bytes

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


Try it online!

• Clever! Nice work. Nov 22, 2022 at 1:03

# Excel (ms365), 169 bytes

=DROP(REDUCE(VSTACK(A1,""),SEQUENCE(LEN(A1)),LAMBDA(a,b,IFERROR(LET(x,LEFT(TAKE(a,1),2),y,IF(--x>25,LEFT(x),x),CHOOSE({1,2},RIGHT(a,LEN(a)-LEN(y)),a&CHAR(y+97))),a))),1)


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. • Having a bit of trouble reproducing your output in MS365 Excel V.2210 - output of B1 is equivalent to ={"","nnnnnn"} Nov 26, 2022 at 20:09
• 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
– JvdV
Nov 26, 2022 at 20:32
• 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 Nov 26, 2022 at 20:41
• I'm going to have a look into that sometime soon when behind a pc 👍
– JvdV
Nov 26, 2022 at 20:53
• @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}.
– 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.

{$[x;(c$97+.n$x),o(n:1+26>.2$x)_x;x]}


Try it online!

• 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 Nov 25, 2022 at 21:55 • 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) Nov 25, 2022 at 22:00
• thanks! i had completely forgotten the n$string overload Nov 25, 2022 at 22:20 # Nibbles, 17 16.5 bytes (33 nibbles) ;$$:+r<;?\<:"26"$$@"a"_>$@

;                                 # 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


ŒṖẈṀ’ỊƲƇV<26Ạ$ƇṪ‘ịØa  Try it online! Or see the test-suite. # Charcoal, 24 bytes Ｗθ«≔⊕‹θ26ι§β✂θ⁰ι≔✂θιＬθ¹θ  Try it online! Link is to verbose version of code. Explanation: Port of @loopywalt's Python solution. Ｗθ«  Loop until the input is empty. ≔⊕‹θ26ι  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. f(*o,*s,u){for(;u=*s++;)*o++=97+((u-=48)>2|u==2&*s>53|!*s?u:u*10+*s++-48);}  Try it online! # C (clang), 64++ bytes u;f(*o,*s){for(;u=*s++;)*o++=*s<54<u-49|!*s?49+u:u*10-431+*s++;}  Try it online! If the output not being null-terminated is a problem, it's 1 extra byte to write to stdout: u;f(*s){for(;u=*s++;)putchar(*s<54<u-49|!*s?49+u:u*10-431+*s++);}  Try it online! # sclin, 32 bytes "2[0-5]|?."\; /# "&":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]|?."\; /# "&":97+ c>S  # jq -Rr, 43 bytes [scan("2[0-5]|?.")|tonumber+97]|implode  Try it online! # ><>, 54 50 bytes ic%:a)?v} <v+*a ~! ^>"a"+o>:2(}:2=}$:@6({*{+l2)*?


Try it online!

Explanation

ic%:a)?v}
Get all the input, mod each by 12 to get numbers and reorder it in the input order.

v
~
>


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

:2(}:2=}\$:@6({*{+l2)*?
If (x < 2 or (x == 2 and y < 6)) and len(stack) > 1

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

"a"+o
Print x + 97 as a character.

# Python 3, 73 71 bytes

lambda s:re.sub('2[0-5]|?.',lambda c:chr(97+int(c)),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 nin'01'or n=='2'and int(n)<6 else chr(97+int(n))+f(n[1:]))


Try it online!

Recursive lambda function.

# Python 3, 135 bytes

def f(n):
if n=='':return''
d=n
if d in'01'or d=='2'and int(n)<6:a,b=n[:2],n[2:]
else:a,b=d,n[1:]
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:
d=n[i]
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])
else:o+=c(n[i])
i+=1
if i<len(n):o+=c(n[i])
return o


Try it online!

Iterative def` function.

• 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. Nov 20, 2022 at 20:10
• 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) Nov 20, 2022 at 23:15
• In TheThonnu's defence: this doesn't really look like it was meant as a competing entry to me. Nov 21, 2022 at 21:27