27
\$\begingroup\$

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
\$\endgroup\$
2
  • \$\begingroup\$ sorry bout the sudden title change. Turns out im stutpid \$\endgroup\$ Jul 21, 2022 at 16:42
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Jul 22, 2022 at 19:34

31 Answers 31

5
\$\begingroup\$

Retina 0.8.2, 32 bytes

[j-s]
a$&
[t-z]
b$&
T`j-z`0a-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.

T`j-z`0a-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.)

\$\endgroup\$
5
\$\begingroup\$

Vyxal, 8 bytes

øAṅ₄ɾkaĿ

Try it Online!

\$\endgroup\$
1
  • \$\begingroup\$ Obvious golf, but ka -> n outside of a context. \$\endgroup\$
    – lyxal
    Aug 12 at 5:49
4
\$\begingroup\$

JavaScript (Node.js), 70 bytes

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

Try it online!

\$\endgroup\$
4
\$\begingroup\$

05AB1E, 10 8 bytes

A0ìDIkSè

I/O as lowercase list of characters.

-2 bytes thanks to @CommandMaster

Try it online or verify all test cases.

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

\$\endgroup\$
2
  • 1
    \$\begingroup\$ A0šDIkSè seems to work for 8 bytes? \$\endgroup\$ Jul 22, 2022 at 12:43
  • \$\begingroup\$ @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. \$\endgroup\$ Jul 22, 2022 at 13:28
4
\$\begingroup\$

><>, 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
\$\endgroup\$
4
\$\begingroup\$

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!

\$\endgroup\$
0
3
\$\begingroup\$

lin, 41 bytes

$a0`,.+10`t2`/\"`rev `_`"`' , `,*10`d 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`,.+10`t2`/\"`rev `_`"`' , `,*10`d tro

Explanation

Prettified code:

$a 0`, dup 10`t 2`/\ ( `rev `_` ) `' , `,* 10`d tro
  • $a 0`, alphabet with 0 in front as a
  • $a 0`,.+ 10`t duplicate and create 0abcdefghi
  • 2`/\ ( `rev `_` ) `' 2-digit pairs (00 0a 0b ... ig ih ii)
  • , `,* 10`d zip with a and remove first 10 pairs ([j a0] [k aa] [l ab] ... [x bd] [y be] [z bf])
  • tro transliterate
\$\endgroup\$
3
\$\begingroup\$

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)
\$\endgroup\$
3
\$\begingroup\$

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.

\$\endgroup\$
2
3
\$\begingroup\$

Perl 5, 27 bytes

s/./-96+ord$&/ge;y/1-9/a-i/

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 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! \$\endgroup\$ Nov 16, 2022 at 12:28
3
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

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
\$\endgroup\$
1
  • 1
    \$\begingroup\$ 13 bytes \$\endgroup\$
    – Shaggy
    Nov 15, 2022 at 11:59
3
\$\begingroup\$

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

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

Code

Try Piet online!

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

Explanation

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

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

Step 3 Add 64, print char and start over.

Step 4 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.

\$\endgroup\$
2
\$\begingroup\$

Charcoal, 13 bytes

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

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

  S             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)
\$\endgroup\$
2
\$\begingroup\$

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!

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 71 bytes

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

Try it online!

\$\endgroup\$
2
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

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!

\$\endgroup\$
2
\$\begingroup\$

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!

\$\endgroup\$
2
\$\begingroup\$

Pyth, 16 bytes

sm@+G0tsdjkmhxGd

Try it online!

\$\endgroup\$
2
\$\begingroup\$

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!

\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$
2
\$\begingroup\$

C (GCC), 101 88 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

\$\endgroup\$
0
2
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

J-uby, 43 bytes

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

Attempt This Online!

\$\endgroup\$
2
\$\begingroup\$

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
\$\endgroup\$
2
\$\begingroup\$

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!

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

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)
\$\endgroup\$
1
  • 1
    \$\begingroup\$ One thing you could do to save some bytes is is =y,1, and then later have W=y]y \$\endgroup\$
    – Sampersand
    Nov 18, 2022 at 17:53
1
\$\begingroup\$

J, 34 32 bytes

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

Try it online!

\$\endgroup\$
0
\$\begingroup\$

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
    )
)
\$\endgroup\$

Your Answer

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

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