Keyboard layouts challenge

14

4

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

QWERTY keyboard

But there are also other keyboard layouts:

DVORAK

DVORAK keyboard

COLEMAK

COLEMAK keyboard

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

Rules

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

Example

DVORAK zZxX as input will give as output ;:qQ

This is so the shortest answer in bytes wins!

user68509

Posted 2017-04-28T15:23:27.133

Reputation:

1Also, some more test cases would be useful. – ETHproductions – 2017-04-28T15:30:13.950

@ETHproductions I'll had some more test cases – None – 2017-04-28T15:37:14.460

When you say "you can use any three distinct values to represent the layouts", do you mean that we can assume the existence of constants containing those layouts? – Tutleman – 2017-04-28T16:42:27.770

1@Tutleman Yes, you can take 0 1 2 for instance – None – 2017-04-28T17:36:03.883

1Wait, 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? – ETHproductions – 2017-04-28T18:38:16.473

@ETHproductions Indeed I didn't think about that aspect, you can edit the question if you want to otherwise I'll do it later :) – None – 2017-04-28T18:42:46.627

There, I think it's better now. – ETHproductions – 2017-04-28T18:46:05.003

To clarify: When you say we can take input as an array, do you mean we can use an array of the individual characters in the string? e.g., ["z","Z","x","X"] – Shaggy – 2017-04-29T09:51:33.043

@Shaggy Yes of course – None – 2017-04-29T09:55:00.773

Cool, that saves me few bytes :) For future reference, when people mention standard means of input in their questions, is a string destructures into an array usually included or is it something that has to be specifically allowed? – Shaggy – 2017-04-29T10:00:15.313

@ETHproductions "they each have to be representable in 10 bytes or less" I'd recommend removing this requirement. – Erik the Outgolfer – 2017-04-29T10:09:01.587

1@EriktheOutgolfer But then you could take e.g. !_#$%&-()*}w[vz0123456789SsW]VZ@AXJE>UIDCHTNMBRL"POYGK<QF:/\‌​=^{`axje.uidchtnmbrl‌​'poygk,qf;?|+~ as input – ETHproductions – 2017-04-29T11:05:15.233

@ETHproductions Maybe add a rule then saying that the input must not be used to construct the layouts, but only to index them? – Erik the Outgolfer – 2017-04-29T13:20:58.783

@EriktheOutgolfer I think that would be a little too blurry. Could I use E, S, and H (the keys on Qwerty-D) as my inputs? Why do you think the 10-byte requirement is not a good idea? – ETHproductions – 2017-04-29T13:54:59.263

1@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. – Erik the Outgolfer – 2017-04-29T13:59:54.913

@EriktheOutgolfer I actually quite like the challenge of trying to use those 10 bytes to do as much as possible! – Jonathan Allan – 2017-04-30T03:40:27.723

Answers

3

Ruby, 258 247 238 bytes

->c,t{c.tr"#{$f='\'"+,-./<=>?[]{'}}:;B-Z_b-z",%W(-_}w[vzW]VZ/=?+SsXJE>UIDCHTNMBRL"POYGK<QF:{xje.uidchtnmbrl'poygk,qf;
#$f}OoBCSFTDHUNEIMKY:QPRGLVWXJZ_bcsftdhuneimky;qprglvwxjz
#$f}IiVMHRTGYUNEOLKP:QWSBFCDXJZ_vmhrtgyuneolkp;qwsbfcdxjz)[t]}

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])
end

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

Tutleman

Posted 2017-04-28T15:23:27.133

Reputation: 571

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. – Shaggy – 2017-04-29T14:11:36.790

@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. – Tutleman – 2017-04-29T20:02:00.810

5

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\$
k=>a=>a.map(c=>1/(t=`1_3-2}w[vz8SsW]VZ1XJE>UIDCHTN0BRL"POYGK<QF:/0=0{1xje.uidchtn0brl'poygk,qf;?0+995Oo6SFTD0UNEI0KY:0PRGL2J8sftd0unei0ky;0prgl2j998Ii5VMHRT0YUNEOLKP:0W0BFCD0J6vmhrt0yuneolkp;0w0bfcd0j5`.replace(/\d/g,n=>15**n)[c.charCodeAt()+k])?c:t)

Try it online!

How it works

Compression

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.

Decompression

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

Translation

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.

Arnauld

Posted 2017-04-28T15:23:27.133

Reputation: 111 334

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. – Shaggy – 2017-04-29T20:39:33.150

4

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.

Neil

Posted 2017-04-28T15:23:27.133

Reputation: 95 035

If the -s represent character ranges, I think you could save some by changing <-@ABC to <-C. – ETHproductions – 2017-04-29T00:21:34.187

@ETHproductions Huh, I wonder why I overlooked it in the upper case version... – Neil – 2017-04-29T00:40:48.587

3

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!

Jörg Hülsermann

Posted 2017-04-28T15:23:27.133

Reputation: 13 026

2

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
p,P=punctuation.split(':;')
w='avmhrtgyuneolkp;qwsbfcdxjz'
c='abcsftdhuneimky;qprglvwxjz'
d="uidchtnmbrl'poygk"
K={'C':D+c+c.upper().replace('Y;','Y:')+p+'Oo'+P,'W':D+w+w.upper().replace('P;','P:')+p+'Ii'+P,'D':D+'axje.'+d+',qf;AXJE>'+d.upper().replace("L'",'L"')+'<QF:!_#$%&-()*}w[vzSsW]VZ@/\=^{`?|+~'}
k,s=input().split()
print''.join(map(lambda x:K[k[0]][Q.index(x)],s))

Dead Possum

Posted 2017-04-28T15:23:27.133

Reputation: 3 256

Well played :) I'm not that good at python so I can't really help you golf this ^^ – None – 2017-04-28T17:37:36.050

2

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.

k=>s=>s.map(c=>`!_#$%&-()*}w[vz${a="0123456789"}SsW]VZ@AXJE>UIDCHTNMBRL"POYGK<QF:/\\=^{\`axje.uidchtnmbrl'poygk,qf;?|+~${b="!\"#$%&'()*+,-./"+a}Oo<=>?@ABCSFTDHUNEIMKY:QPRGLVWXJ${d="Z[\\]^_\`a"}bcsftdhuneimky;qprglvwxjz{|}~${b}Ii<=>?@AVMHRTGYUNEOLKP:QWSBFCDXJ${d}vmhrtgyuneolkp;qwsbfcdxjz{|}~`[`${b}:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXY${d}bcdefghijklmnopqrstuvwxyz{|}~`.search(c)+k]).join``

Try It

f=
k=>s=>s.map(c=>`!_#$%&-()*}w[vz${a="0123456789"}SsW]VZ@AXJE>UIDCHTNMBRL"POYGK<QF:/\\=^{\`axje.uidchtnmbrl'poygk,qf;?|+~${b="!\"#$%&'()*+,-./"+a}Oo<=>?@ABCSFTDHUNEIMKY:QPRGLVWXJ${d="Z[\\]^_\`a"}bcsftdhuneimky;qprglvwxjz{|}~${b}Ii<=>?@AVMHRTGYUNEOLKP:QWSBFCDXJ${d}vmhrtgyuneolkp;qwsbfcdxjz{|}~`[`${b}:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXY${d}bcdefghijklmnopqrstuvwxyz{|}~`.search(c)+k]).join``
o.innerText=f(j.value=0)([...i.value="zZxX"])
i.oninput=j.oninput=_=>o.innerText=f(+j.value)([...i.value].filter(c=>`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~`.includes(c)))
<select id=j><option value=0>DVORAK<option value=94>COLEMAK<option value=188>WORKMAN</select><input id=i><pre id=o>

Shaggy

Posted 2017-04-28T15:23:27.133

Reputation: 24 623

Actually I compiled the strings :P – ETHproductions – 2017-04-28T18:43:56.317

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`` – ETHproductions – 2017-04-28T18:51:12.483

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. – Shaggy – 2017-04-28T23:08:27.940

1

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
std::map<char,char>t[4];
char*c[4]={
"\"'+,-./:;<=>?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]));
}

Extra: Qwerty -> Dvorak Only (197 bytes)

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

#include<string>
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])]);}

JackMacWindows

Posted 2017-04-28T15:23:27.133

Reputation: 21

463 bytes – ceilingcat – 2020-02-26T21:38:00.093

1

05AB1E, 199 192 187 bytes

žQDIiA¦'mKDuì“"'+,-./:;<=>?[]_{}“«.•4Zl˜η‡#ûwj˜ÐAδO•Duìð“>"<:.',;“S.;“_-}w[vzSsW]VZ/={?+“«ë¹<i.•2¸3'ÐQïK¤)•Duì„:;©ì.•C^ÿ¶₆*γŽ¨ï•Duì„Ooìë.•Brì·=ζW˜ΛlÝδ•Duì®ì.•]X)àƒ₆ä¤QúØM•Duì„Iiì}ð®S.;}‡‡

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.

Explanation:

ž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 “"'+,-./:;<=>?[]_{}“
     .•4Zl˜η‡#ûwj˜ÐAδO• 
                      #  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.
     “_-}w[vzSsW]VZ/={?+“«
                      #  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.

Kevin Cruijssen

Posted 2017-04-28T15:23:27.133

Reputation: 67 575

0

C, 394 bytes

Try Online

char*K[3]={"!_#$%&-()*}w[vz0123456789SsW]VZ@AXJE>UIDCHTNMBRL\"POYGK<QF:/\\=^{`axje.uidchtnmbrl'poygk,qf;?|+~",
"!\"#$%&'()*+,-./0123456789Oo<=>?@ABCSFTDHUNEIMKY:QPRGLVWXJZ[\\]^_`abcsftdhuneimky;qprglvwxjz{|}~",
"!\"#$%&'()*+,-./0123456789Ii<=>?@AVMHRTGYUNEOLKP:QWSBFCDXJZ[\\]^_`avmhrtgyuneolkp;qwsbfcdxjz{|}~"};
main(int c,char**v){char*t=v[2];while(*t)putchar(*t>32?K[atoi(v[1])][*t-33]:*t),t++;}

Khaled.K

Posted 2017-04-28T15:23:27.133

Reputation: 1 435

388 bytes – ceilingcat – 2020-02-22T00:14:12.053