The keyboard layout people commonly use is the QWERTY layout as shown below.

QWERTY keyboard

But there are also other keyboard layouts:


DVORAK keyboard


COLEMAK keyboard


Your task

Your code will take two inputs: the name of the keyboard layout and a string to transcribe. Your goal is to convert your QWERTY input as if you were typing with the keyboard layout given as first parameter.


The input format is free, you can use strings, arrays, etc. Moreover, you can use any three distinct values to represent the layouts to reduce your byte count, but they each have to be representable in 10 bytes or less.

You need only to handle the keys with a white background. Specifically, you must transpose the printable ASCII characters from the QWERTY alphabet to one of the other alphabets:

 QWERTY: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
 DVORAK: !_#$%&-()*}w[vz0123456789SsW]VZ@AXJE>UIDCHTNMBRL"POYGK<QF:/\=^{`axje.uidchtnmbrl'poygk,qf;?|+~
COLEMAK: !"#$%&'()*+,-./0123456789Oo<=>?@ABCSFTDHUNEIMKY:QPRGLVWXJZ[\]^_`abcsftdhuneimky;qprglvwxjz{|}~
WORKMAN: !"#$%&'()*+,-./0123456789Ii<=>?@AVMHRTGYUNEOLKP:QWSBFCDXJZ[\]^_`avmhrtgyuneolkp;qwsbfcdxjz{|}~

(Note: this was transcribed by hand by @ETHproductions, so if you see any errors, please point them out!)


DVORAK zZxX as input will give as output ;:qQ

This is so the shortest answer in bytes wins!

  • 1
    \$\begingroup\$ Also, some more test cases would be useful. \$\endgroup\$ Apr 28, 2017 at 15:30
  • 1
    \$\begingroup\$ @Tutleman Yes, you can take 0 1 2 for instance \$\endgroup\$
    – user68509
    Apr 28, 2017 at 17:36
  • 1
    \$\begingroup\$ Wait, can we take e.g. !_#$%&-()*}w[vz0123456789SsW]VZ@AXJE>UIDCHTNMBRL"POYGK<QF:/\=^{`axje.uidchtnmbrl'poygk,qf;?|+~ as the input for the Dvorak keyboard? That would trivialize the challenge... How about introducing a length restriction or something similar? \$\endgroup\$ Apr 28, 2017 at 18:38
  • 1
    \$\begingroup\$ @EriktheOutgolfer But then you could take e.g. !_#$%&-()*}w[vz0123456789SsW]VZ@AXJE>UIDCHTNMBRL"POYGK<QF:/\‌​=^{`axje.uidchtnmbrl‌​'poygk,qf;?|+~ as input \$\endgroup\$ Apr 29, 2017 at 11:05
  • 1
    \$\begingroup\$ @ETHproductions Yes you could use those characters as inputs, although the layouts must not depend on them at all to be constructed. The only use for them would be to choose which layout to use. 10 bytes is a fixed limit (IMO too short), that's why I don't like it. And yes you say bytes not chars. I think this should've been sandboxed for more time. \$\endgroup\$ Apr 29, 2017 at 13:59

9 Answers 9


JavaScript (ES7), 282 273 251 250 bytes

Takes a keyboard layout ID k and an array of characters a in currying syntax (k)(a). Returns an array of translated characters.

The layout IDs are:

  • DVORAK: \$-32\$
  • COLEMAK: \$64\$
  • WORKMAN: \$160\$

Try it online!

How it works


All three target layouts are stored in a single compressed string, where each character is either:

  • a translation character from QWERTY
  • a digit representing the number of consecutive characters that do not need to be translated

More specifically, a digit \$n\$ is interpreted as the length of the number \$15^n\$ in base \$10\$:

n | 15**n       | length
0 | 1           | 1
1 | 15          | 2
2 | 225         | 3
3 | 3375        | 4
4 | 50625       | 5 (not used)
5 | 759375      | 6
6 | 11390625    | 8
7 | 170859375   | 9 (not used)
8 | 2562890625  | 10
9 | 38443359375 | 11

For instance, #$%&-()* in DVORAK is stored as 3-2 because #$%& and ()* have identical mappings in QWERTY and only - is an actual translation.

In particular, 0123456789 is mapped the same way on all layouts and never has to be translated. Therefore, there's no possible ambiguity between a digit used for compression and a digit used for translation.


To decompress the layout string, we replace each digit \$n\$ with \$15^n\$. For instance, 3-2 is decompressed as 3375-225.


For each character c in a, we extract the translation character t, using k as an offset in the uncompressed layout string, and test whether it's a digit with 1/t. If so, we output the original character c instead.

  • \$\begingroup\$ I had to run downstairs to put on a hat just so I could doff it to you! I knew someone would beat me but I'm happy to have been beaten in style. You shall have my upvote just as soon as I've finished digesting exactly how you pulled this masterpiece off. \$\endgroup\$
    – Shaggy
    Apr 29, 2017 at 20:39

Retina, 273 270 bytes

T`p` !_#-&\-()*}\w[vzdSsW]VZ@AXJ\E>UIDC\HTNMBR\L"P\OYGK<QF:/\\=^{\`axje.ui\dc\htnmbr\l'\p\oygk,qf;?|+~`^D.*
T`p` -9\O\o<-CSFTD\HUN\EIMKY:QPRG\LVWXJZ-csft\d\huneimky;q\prg\lv-xjz-~`^C.*
T`p` -9Ii<-AVM\HRTGYUN\E\O\LKP:QWSBFCDXJZ-avm\hrtgyune\o\lk\p;q\wsbfc\dxjz-~`^W.*

Try it online! Prefix the message with a single letter D, C or W for the desired keyboard layout. Unfortunately Retina supports a bunch of magic letters (p being the obvious one, but I did manage to slip in a d) which all need to be quoted, except I was able to use v-x instead of v\wx. Edit: Saved 3 bytes thanks to @ETHproductions.

  • \$\begingroup\$ If the -s represent character ranges, I think you could save some by changing <-@ABC to <-C. \$\endgroup\$ Apr 29, 2017 at 0:21
  • \$\begingroup\$ @ETHproductions Huh, I wonder why I overlooked it in the upper case version... \$\endgroup\$
    – Neil
    Apr 29, 2017 at 0:40

Ruby, 258 247 238 bytes


Try it online!

This is a function taking two arguments: the message to be swapped, and a value 0-2 representing the layout to be swapped to, where 0 corresponds to Dvorak, 1 to Colemak, and 2 to Workman.

Fundamentally, I don't think this is much different than the other answers. More readably, it looks like this:

def swap_layout(message, layout)
    keyboards = [DVORAK, COLEMAK, WORKMAN] # Omitted here for brevity
    return message.tr(QWERTY, keyboards[layout])

Ruby's string#tr function takes two arguments: a string containing characters to be replaced, and a string containing their replacements. Helpfully, it allows you to specify ranges of characters using a-z syntax. The other key space-saving realization is that it's not necessary to include characters that are the same in all four layouts, which allowed me to get rid of all digits, the letter "A" in both upper- and lowercase, and a handful of special characters.

One other weird bit of syntax is the use of %W(). This creates an array of strings containing everything inside the parentheses, separated by whitespace. All linebreaks in the submission actually function as element separators. %W() also permits string interpolation (which is done with the #{} operator) - %w() would've been the same thing, but without string interpolation.

I'd also like to take a moment to blame Dvorak for messing with my plans for optimization through its insistence on being totally different than everyone else, all the time; a Qwerty/Colemak/Workman solution could have been so beautifully short...

  • \$\begingroup\$ Does taking an integer as representation of the keyboard to map to meet the requirements? The test case seems to explicitly require the name of the keyboard but, then, the rules would seem to allow any input to represent the keyboard. \$\endgroup\$
    – Shaggy
    Apr 29, 2017 at 14:11
  • \$\begingroup\$ @Shaggy To quote one of the asker's comments on the post, "you can take 0 1 2 for instance", which is precisely what I've done. \$\endgroup\$
    – Tutleman
    Apr 29, 2017 at 20:02

PHP, 364 Bytes

echo strtr($argv[2],($t=[[":;BCDEFHIJKLMNOPRTUVWYbcdefhijklmnoprtuvwy","IiVMHRTYUNEOLKP:WBFCDJvmhrtyuneolkp;wbfcdj"],[":;DEFGIJKLNOPRSTUYdefgijklnoprstuy","OoSFTDUNEIKY:PRGLJsftduneiky;prglj"],["\"'+,-./:;<=>?BCDEFGHIJKLNOPQRSTUVWXYZ[]_bcdefghijklnopqrstuvwxyz{}","_-}w[vzSsW]VZXJE>UIDCHTNBRL\"POYGK<QF:/={xje.uidchtnbrl'poygk,qf;?+"]][ord($argv[1])%3])[0],$t[1]);

Array contains 3 arrays where key stands for 0=W, 1=C, 2=D

Try it online!


Python 2, 422 bytes

Tried to combine layouts with some clever way, but it didn't help much.
Try it online

from string import*
Q=' '+printable
D=' '+digits
print''.join(map(lambda x:K[k[0]][Q.index(x)],s))
  • \$\begingroup\$ Well played :) I'm not that good at python so I can't really help you golf this ^^ \$\endgroup\$
    – user68509
    Apr 28, 2017 at 17:37

JavaScript (ES6), 461 409 404 395 385 bytes

I wrote the original version of the below up on my phone while sitting on a bus and then ran out of time to properly golf it so there's more crunching to follow. Thanks to @ETHproductions for the help so far.

The Keyboard strings were copied directly from the question so blame Antoine ETH for any errors!

This takes an integer representing the keyboard layout (0 for DVORAK, 94 for COLEMAK & 188 for WORKMAN) and an array of the string as arguments by currying - e.g., f(0)(["z","Z","x","X"]) outputs ;:qQ.


Try It

<select id=j><option value=0>DVORAK<option value=94>COLEMAK<option value=188>WORKMAN</select><input id=i><pre id=o>

  • \$\begingroup\$ Actually I compiled the strings :P \$\endgroup\$ Apr 28, 2017 at 18:43
  • \$\begingroup\$ Let's see... you'll never get QWERTY as input, so I think you could save some bytes by not storing the Object in a variable. It'd look something like s=>[...s].map(c=>({D:"...",C:"...",W:"..."}[k[0]]["...".indexOf(c)]).join`` \$\endgroup\$ Apr 28, 2017 at 18:51
  • \$\begingroup\$ Aha, yes, I knew there was a way to work directly off the object, within the map() but I ran out of time - believe it or not, I wrote this up while sitting on the bus to the cinema. I'll update it with your suggestion and a few improvements I've spotted myself tomorrow. \$\endgroup\$
    – Shaggy
    Apr 28, 2017 at 23:08

05AB1E, 199 192 187 bytes


Uses IDs 1 for DVORAK; 2 for COLEMAK and 3 for WORKMAN.
Pushes the ID first, then the string we want to transliterate.

Try it online or verify full ASCII-range for all three.


žQ                    # Push printable ASCII builtin string:
                      #  “ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~“
  D                   # Duplicate it
   Ii                 # If the input-integer is 1:
     A                #  Push the lowercase alphabet
      ¦'mK           '#  Remove the first character (the 'a') and 'm'
          Duì         #  Prepend an uppercase copy
                     "#  And then append the string “"'+,-./:;<=>?[]_{}“
                      #  Push compressed string "xje uidchtnbrl poygk qf "
        Duì           #  Prepend an uppercase copy
     ð                #  Push a space " "
      “>"<:.',;“S    "#  Push the characters [">",'"',"<",":",".","'",",",";"]
                 .;   #  Replace the first space with ">", second with '"', etc.
                      #  And then append the string “_-}w[vzSsW]VZ/={?+“
   ë                  # Else:
    ¹<i               #  If the input-integer is 2 instead: 
       .•2¸3'ÐQïK¤)• '#   Push compressed string "defgijklnoprstuy"
          Duì         #   Prepend an uppercase copy
       „:;            #   Push string ":;"
          ©           #   Store it in the register (without popping)
           ì          #   And then prepend this ":;" in front of the "DEF...def..."-string
       .•C^ÿ¶₆*γŽ¨ï•  #   Push compressed string "sftduneiky prglj"
          Duì         #   Prepend an uppercase copy
       „Ooì           #   And then prepend "Oo"
    ë                 #  Else (so the input-integer is 3):
     .•Brì·=ζW˜ΛlÝδ•  #   Push compressed string "bcdefhijklmnoprtuvwy"
        Duì           #   Prepend an uppercase copy
     ®ì               #   And then prepend ":;" (from the register)
     .•]X)àƒ₆ä¤QúØM•  #   Push compressed string "vmhrtyuneolkp wbfcdj"
        Duì           #   Prepend an uppercase copy
     „Iiì             #   And then prepend "Ii"
    }                 #  After the inner if-else:
     ð                #  Push a space " "
      ®S              #  Push the characters [":",";"] (from the register)
        .;            #  Replace the first space with ":" and the second space with ";"
   }                  # After the outer if-else:
    ‡                 # Transliterate the two strings we created,
                      #  in the duplicate printable ASCII string
     ‡                # And then transliterate the printable ASCII string with this string,
                      #  in the (implicit) string-input
                      # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to compress strings not part of the dictionary?) to understand how the compressed strings work.


C++, 528 bytes

#include <map>
#define M(a,b)a!='\0'?a:b
std::map<char,char>t[3];char*c[]={"\"'+,-./:;<=>?BCDEFGHIJKLMNOPQRSTUVWXYZ[]_bcdefghijklmnopqrstuvwxyz{}","_-}w[vzSsW]VZXJE>UIDCHTNMBRL\"POYGK<QF:/={xje.uidchtnmbrl'poygk,qf;?+","\"'+,-./Oo<=>?BCSFTDHUNEIMKY:QPRGLVWXJZ[]_bcsftdhuneimky;qprglvwxjz{}","\"'+,-./Ii<=>?VMHRTGYUNEOLKP:QWSBFCDXJZ[]_vmhrtgyuneolkp;qwsbfcdxjz{}"};int main(int d,char*v[]){for(int i=1;i<4;i++)for(int j=0;j<68;j++)t[i-1][c[0][j]]=c[i][j];for(int k=0;v[2][k];k++)printf("%c",M(t[v[1][0]-'0'][v[2][k]],v[2][k]));}

Run with ./multitrans <0-2> <string> where 0=Dvorak, 1=Colemak, and 2=Workman.

I added newlines in between to make the code slightly more readable below. This code generates a translation map from the strings where looking up the Qwerty character returns the translation (t[0]['s'] = 'o' for s into Dvorak), and then it uses the map to translate. The translation strings are shortened because some characters don't need to be changed ever. It could probably be reduced further, though.

#include <map>
#define M(a,b)a!='\0'?a:b
int main(int d,char*v[]){
    for(int i=1;i<4;i++)
        for(int j=0;j<68;j++)
    for(int k=0;v[2][k];k++)

Extra: Qwerty -> Dvorak Only (197 bytes)

I recently wrote this code that changes Qwerty into Dvorak, though capitals go untranslated.

char*d="\',.pyfgcrlaoeuidhtns;qjkxbmwvz ";std::string t="qwertyuiopasdfghjkl;zxcvbnm,./ ";int main(int c,char*v[]){for(int j=0;j<strlen(v[1]);j++)printf("%c",d[t.find(v[1][j])]);}

C, 394 bytes

Try Online

main(int c,char**v){char*t=v[2];while(*t)putchar(*t>32?K[atoi(v[1])][*t-33]:*t),t++;}

Your Answer

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