# Letters to numbers to letters (poorly)

Every now and again, I'll encounter a large string of alphabetical text encoded as numbers from 1-26. Before I knew that I could just google a translator online, I had to personally, painstakingly copy and paste the entirety of the encoded text into a text document and translate it myself.

Thankfully, I did know about find-and-replace features, which I used to search for all copies of each number and convert them to their corresponding characters. Only one problem... If you do this in numerical order, it will come out garbled.

This is because the single digit numbers used to encode a-i also appear as part of the remaining two digit numbers encoding j-z. For example, 26 should decode to z, but since both 2 and 6 are less than 26, we decode those first. 2 becomes b, and 6 becomes f, so 26 becomes bf.

Note also that for 10 and 20, there is no conversion for 0, so we end up with a0 and b0 respectively.

## Challenge

Given a string of alphabetical characters (abcdefghijklmnopqrstuvwxyz), output the result of converting to numbers and back again using the algorithm described above. You may assume input is all lowercase or all uppercase. This is , so shortest code in bytes wins.

Important: You may not zero index.

Full conversion list:

a: a
b: b
c: c
d: d
e: e
f: f
g: g
h: h
i: i
j: a0
k: aa
l: ab
m: ac
n: ad
o: ae
p: af
q: ag
r: ah
s: ai
t: b0
u: ba
v: bb
w: bc
x: bd
y: be
z: bf


## Examples

xyzzy => bdbebfbfbe
caged => caged
jobat => a0aebab0
swagless => aibcagabeaiai

• sorry bout the sudden title change. Turns out im stutpid Jul 21, 2022 at 16:42
• Not really impacting the question, but if you don't do it in numerical order it will still come out garbled unless you have separated your numbers somehow. For instance if you go in reverse order you will replace 26 while these may actually have been a 2 and a separate 6. Jul 22, 2022 at 19:34

# Retina 0.8.2, 32 bytes

[j-s]
a$& [t-z] b$&
Tj-z0a-i0l


Try it online! Link includes test cases. Explanation:

[j-s]
a$& [t-z] b$&


Precede the letters j-z with an a or b as appropriate.

Tj-z0a-i0l


Translate them to 0 or a-i as appropriate. (l expands to a-z but only a-f get used as the replacement list gets truncated to the length of the translation list.)

# Vyxal, 8 bytes

øAṅ₄ɾkaĿ


Try it Online!

• Obvious golf, but ka -> n outside of a context. Aug 12, 2023 at 5:49

# JavaScript (Node.js), 70 bytes

s=>s.replace(r=/./g,c=>parseInt(c,36)-9).replace(r,i=>'0abcdefghi'[i])


Try it online!

# 05AB1E, 10 8 bytes

A0ìDIkSè


I/O as lowercase list of characters.

-2 bytes thanks to @CommandMaster

Explanation:

A         # Push the lowercase alphabet
0ì       # Prepend a leading "0"
D      # Duplicate it
I     # Push the input character-list
k    # Get the (0-based) index of each character in the "0abc...xyz" string
S   # Convert it to a flattened list of digits
è  # (0-based) index each into the "0abc...xyz" string
# (after which the result is output implicitly)


If None is a valid output for 0, builtin .b could be used for an alternative 8-byter in the legacy version of 05AB1E, with uppercase character-lists as I/O: try it online. (The .b results in "@" in the new version of 05AB1E for 0: try it online.)

• A0šDIkSè seems to work for 8 bytes? Jul 22, 2022 at 12:43
• @CommandMaster Ah, of course.. Why not just prepend instead of append so fixing the 0-based to 1-based indexing manually with >/< isn't necessary. It looks so simple now that I see it.. Thanks. Jul 22, 2022 at 13:28

# ><>, 35 bytes

i"":@-:9)?\+o
"*-$a,""+o\:a%:0="0  Try it online Explanation i"":@- # get input and subtract 96 from it to convert letters to 1-indexed numbers and save a copy of 96 for reverse conversion later :9)?\+o # if number is > 9 move down, else print with saved 96 added \:a% # mod a copy of the number by 10 "*- :0="0 # if the result is 0, subtract 48$a,""+o           # divide the other copy by 10 and print with 96 added
\          # handle the print of the mod copy on the first row


# C (clang), 74 67 bytes

• -6 bytes thanks to @jdt.
p(c){c&&putchar(c^96);}c;f(*s){for(;c=*s++%96;p(c%10?:80))p(c/10);}


Try it online!

# lin, 41 bytes

$a0,.+10t2/\"rev _"' , ,*10d tro  Try it here! Decided to go for a different non-regex approach, which came out quite nicely. Turns out that tro surprisingly accepts an iterator of iterators, which saved quite a few bytes. For testing purposes: "jobat" ; outln$a0,.+10t2/\"rev _"' , ,*10d tro


## Explanation

Prettified code:

$a 0, dup 10t 2/\ ( rev _ ) ' , ,* 10d tro  • $a 0, alphabet with 0 in front as a
• $a 0,.+ 10t duplicate and create 0abcdefghi • 2/\ ( rev _ ) ' 2-digit pairs (00 0a 0b ... ig ih ii) • , ,* 10d zip with a and remove first 10 pairs ([j a0] [k aa] [l ab] ... [x bd] [y be] [z bf]) • tro transliterate # Husk, 14 bytes m!₁ṁod€₁ …"az0  Try it online! Inspired by Kevin Cruijssen's approach, although comes-out a bit longer in Husk... … # Fill-in the gaps with character ranges "az0 # in the string "az0" # (fill-in works both backwards & forwards, resulting in: # "abcde...vwxyzyxwv...edcba_^]\...43210") # = assign this as '₁' ṁo # Now, for each input letter €₁ # get the index of first occurence in ₁ (=index in alphabet) d # and split the decimal digits; m # Then, for each digit !₁ # retrieve the element in ₁ using modular 1-based indexing # (so zero retrieves the last element)  # R, 75 bytes \(s,[=gsub)chartr("j-z","0a-i0a-i","([j-s])"["a\\1","([t-z])"["b\\1",s]]) Attempt This Online! Straightforward replacement. ### R, 84 81 bytes Edit: -3 bytes thanks to @Dominic van Essen. \(s)gsub("",0,intToUtf8(strtoi(unlist(strsplit(paste(utf8ToInt(s)-96),"")))+96)) Attempt This Online! Going with the algorithm. • as.double -> strtoi for the second one... Jul 22, 2022 at 11:53 • See here for a shorter approach: codegolf.stackexchange.com/a/250188/55372 Jul 22, 2022 at 14:25 # Perl 5, 27 bytes s/./-96+ord$&/ge;y/1-9/a-i/


Try it online!

• Nice answer! Much more insightful than mine, I was trying to use globs! You can save one extra byte using ; as the separator for y/// though: Try it online! Nov 16, 2022 at 12:28

# Lexurgy, 92 bytes

a:
{j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}=>{a\0,aa,ab,ac,ad,ae,af,ag,ah,ai,b\0,ba,bb,bc,bd,be,bf}


Naive 1-1 substitution.

# Japt, 13 bytes

;£ÒCaXÃ®°gCi0


Try it

;£ÒCaXÃ®°gCi0
;             # C = "abcdefghijklmnopqrstuvwxyz"
£            # For each letter in the input:
CaX        #  Find its 0-based index in C
Ò           #  Add 1
Ã       #  Join to a string
®      # For each character in the new string
°     #  Coerce to a number
g    #  Get the character at that index in:
Ci0 #   C with a 0 added to the front

• 13 bytes Nov 15, 2022 at 11:59

# Piet + ascii-piet, 129 bytes (5×27=135 codels)

uqijsvudnbddt   feussskiu L?q   sa   dttlvqfeumdctss?Lr????saa???????????????s  Lkjftrqavqcefcnbftmnkss K                    ssvc


Try Piet online!

Input the string in upper-case with the sentinel value @.

Explanation

Read char from input, subtract 64 and check if it is greater than 9.

If c<=9: check if its check if it is greater than 0 (else terminate).

Add 64, print char and start over.

If c>9: duplicate, push 10 on stack, duplicate, roll, divide, print char, modulo and check if it is greater than 0.

If c>0: print char (as above) and start over.

If c=0: print number and start over.

# Charcoal, 13 bytes

⭆⭆Ｓ⊕⌕βι§⁺β0⊖ι


Try it online! Link is to verbose version of code. Explanation:

  Ｓ             Input string
⭆              Map over characters and join
⌕           Index of
ι         Current character
β          In lowercase alphabet
⊕            Incremented
⭆               Map over characters and join
β      Lowercase alphabet
⁺       Concatenated with
0     Literal string 0
§        Indexed by
ι   Current character
⊖    Decremented (auto-casts to integer)


# Factor, 53 bytes

[ 96 v-n [ >dec ] map-concat 48 v+n "" "0" replace ]


Needs modern Factor for >dec but here's a version that works on TIO for +1 byte:

Try it online!

# JavaScript (ES6), 71 bytes

s=>s.replace(/[j-z]/g,c=>"ab"[c>'s'|0]+"abcdefghi0"[parseInt(c,36)%10])


Try it online!

# Burlesque, 25 bytes

)**96?-imXX@azr@'0+]jsi\[


Try it online!

)**   # Map ord(a)
96?-  # Subtract 96 (get index into alphabet)
imXX  # Concat and separate by digit
@azr@ # Alphabet [a,b,..,z]
'0+]  # Prepend [0,a,b,..,z]
jsi   # Select indices
\[    # Concat


# Python 3, 79 bytes

lambda s:s.translate({n:'AB'[n>83]+'0ABCDEFGHI'[n%74%10]for n in range(74,91)})


Try it online!

# Bash, 74 47 bytes

sed "s/[j-s]/a&/g;s/[t-z]/b&/g"|tr j-z 0a-i0a-i


Try it online!

-27 thanks to Neil's idea

# first approach, 74 bytes

sed "s/./\"'&\"/g"|xargs printf '%d-96\n'|bc|sed '/../s/./&\n/'|tr 1-9 a-j


Try it online!

# Pyth, 16 bytes

sm@+G0tsdjkmhxGd


Try it online!

# Python 3.8 (pre-release), 76 bytes

f=lambda s:s and'ba'[2-(q:=ord(s[0])%32)//10::2]+'0abcdefghi'[q%10]+f(s[1:])


Try it online!

# R, 68 bytes

\(x,y=utf8ToInt(x)-96)intToUtf8(rbind(y/10+96*(y>9),48--y%%10%%-58))

Attempt This Online!

Calculates 2 decimal digits for each letter, and then calculates the corresponding codepoint values, setting unused values (leading zeros) to zero, which is not output by the intToUtf8 function.

# R, 54 bytes

\(x)chartr('1-9','a-i',Reduce(paste0,utf8ToInt(x)-96))

Attempt This Online!

Port of Kjetil S's Perl answer.

# C (GCC), 10188 87 bytes

f(char*s){for(char*x;*s;)for(asprintf(&x,"%d",*s++-64);*x;)putchar(*x+(*x++^48?16:0));}


Attempt This Online!

Prints to standard out. Uses asprintf, which is a GNU-specific function and may or may not require a definition in the header, so I don't know if it's okay.

This is basically the first C function I've ever written, so there are almost certainly ways this can be improved.

-13 bytes from @c-- by iterating one character at a time, removing parens, and changing the ternary
-1 bytes from @ceilingcat by moving the char*x declaration into the for statement

# Dyalog APL, 32 bytes

{(⎕A,'0')[27@(0∘=)⍎¨∊⍕¨⎕A⍳⍥⎕C⍵]}


Attempt This Online!

                       ⎕A⍳   ⍵  find each character's index in the alphabet
⍥⎕C   case insensitively (casefold both arguments first)
⍕¨         turn each number into string
∊           flatten nested array to get the individual digits
⍎¨            turn each digit back into a number
27@(0∘=)              turn 0s into 27s
[                    ] index into
⎕A                            the alphabet
,'0'                        with a 0 at the end at the 27th position


# J-uby, 43 bytes

~(:gsub+(:ord|:+&-96))&/./|~:tr&"a-i"&"1-9"


Attempt This Online!

# Japt, 12 bytes

c_uH sAÇ©d96


Try it (Includes all test cases)

c_uH sAÇ©d96     :Implicit input of string
c_               :Map codepoints
u              :  Modulo
H             :    32
s           :  Convert to string in base
A          :    10
Ç         :    Map the range [0,A)
©        :      Logical AND with
d96     :      Add 96 and get character at that codepoint


# Julia 1.7, 52 bytes

!x=replace(join([x...].-''),('1':'9'.=>'a':'i')...)


Attempt This Online!

• replace supports multiple patterns as of Julia 1.7

# Julia 1.0, 66 65 bytes

~x=join(get.([Dict('1':'9'.=>'a':'i')],[join([x...].-'')...],0))


Try it online!

• -1 byte by using [Dict(...)] Nov 17, 2022 at 15:04
• There is Julia 1.7.3 on ato.pxeger.com Nov 17, 2022 at 15:05

# Knight (v2), 49 46 bytes

;=y,=xP;Wx;=y+y-Ax 96=x]x;W=y]y=x+x&[yA+96[yOx


Try it online!

This uses the quirky nature of +(List, Number). It acts similarly to appending to a String where it will append individual digits, with the exception that the elements will be Number instead of String.

So for example, + @ 42 will result in [4, 2], compared to + "" 42 which will be "42" or + @ "42" which will be ["4", "2"].

This has a bunch of perks:

• Creating a list is only one char (@ for empty, , with an existing expression), compared to empty string which is 2 ("")
• The digits will be a Number, so there is no need to convert from ASCII to do char math. I can therefore use the & operator to both test if the digit is 0 without a compare, and append that 0 if it is due to coercion rules.
; = y ,                             # Set y to a list with one dummy argument (which is x)
: = x PROMPT()              # Read a string into x

# Convert to a list of numbers containing each digit (with a preceeding dummy value)
; WHILE (x)                         # While x is not empty
; = y + y (- ASCII (x) 96)      # Push DIGITS of x[0] - 'a' - 1 as Number to y
: = x ] x                       # Pop front char from x
# Convert back
# x = "", y = [ dummy, digit, digit ]
; WHILE (= y ] y)                   # While y is not empty, popping front beforehand
: = x + x (                     # Append to x...
& [ y                        #   Either y[0] if it is zero (which is coerced to '0')
: ASCII (+ 96 ([ y))         #   or y[0] + 'a' - 1 converted to ASCII
: )
: OUTPUT x                          # Output the string in x

• -3 bytes for taking Samp's advice and making y for WHILE(= y ] y)
• One thing you could do to save some bytes is is =y,1, and then later have W=y]y Nov 18, 2022 at 17:53

# J, 34 32 bytes

"."0@(,&":/)&.:(('0',97}.a.)&i.)


Try it online!

# Batch, 432 Bytes

SETLOCAL ENABLEDELAYEDEXPANSION&(CMD /U /C^"<NUL SET /P=abcdefghijklmnopqrstuvwxyz^"|FIND /V ""|FINDSTR /N "^")>a&(CMD /U /C^"<NUL SET /P=%s%^"|FIND /V "")>t&FOR /F %%Q in (t)DO (FOR /F "tokens=1-2 delims=:" %%A in ('FINDSTR /RC:"%%Q" a')DO (IF %%A GEQ 20 (SET /Ad=%%A+76&CMD /CEXIT !d:96=48!&<NUL SET /P=b!=exitcodeAscii!) else IF %%A GEQ 10 (SET /Ad=%%A+86&CMD /CEXIT !d:96=48!&<NUL SET /P=a!=exitcodeAscii!) else <NUL SET /P=%%B))


Assuming @ECHO OFF and passing it as

CALL golf.bat swagless


produces

aibcagabeaiai


This can probably not the best way, but I thought that this was a pretty cool way to do it. Ungolfed:

(CMD /U /C^"<NUL SET /P=abcdefghijklmnopqrstuvwxyz^"|FIND /V ""|FINDSTR /N "^")>a
(CMD /U /C^"<NUL SET /P=%1^"|FIND /V "")>t

FOR /F %%Q in (t) DO (
FOR /F "tokens=1-2 delims=:" %%A in ('FINDSTR /RC:"%%Q" a') DO (
IF %%A GEQ 20 (
SET /Ad=%%A+76
CMD /CEXIT !d:96=48!
<NUL SET /P=b!=exitcodeAscii!
) else IF %%A GEQ 10 (
SET /Ad=%%A+86
CMD /CEXIT !d:96=48!
<NUL SET /P=a!=exitcodeAscii!
) else <NUL SET /P=%%B
)
)