Keyboard layouts challenge

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.


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.


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