# Can I Type This With My Left Hand?

### Inspiration

The longest words that can be typed with only the left hand on a standard QWERTY keyboard are "sweaterdresses", "tesseradecades", and "aftercataracts" (Source: Wikipedia).

### Challenge

Given as input a "keyboard" $$\K\$$ and a string $$\S\$$ determine whether $$\S\$$ can be typed using only the left hand on keyboard $$\K\$$.

### Input

The keyboard $$\K\$$ will be provided as a list of 3 rows. You may take this input in any reasonable format (eg. a list of 3 strings, a list of 3 lists of characters, etc.), but please do not take the left-hand-side and right-hand-side of the keyboard separately, as that defeats the purpose of the challenge.

You may assume that the input contains only lowercase letters (or only uppercase letters if you wish). Each of the three rows of the keyboard may be of any non-zero size, but each letter from a-z will appear only once on the keyboard.

Example 1: [qwertyuiop, asdfghjkl, zxcvbnm]

Example 2: [qazwsxplkm, edcrfv, tgbyhnuji]

The string $$\S\$$ may also be taken as input in any reasonable format.

### Output

Output a truthy value if the string $$\S\$$ can be typed using the left hand on the keyboard $$\K\$$ and a falsey value otherwise.

For the purposes of this challenge: A word can be typed with the left hand if it is made up solely of letters appearing in the first half of each row of the keyboard. If a row contains an odd number of letters, the middle letter is also included in the first half.

Using the row asdfghjkl as an example, the word gafs can be typed with the left hand.

### Scoring

This is . Make your code as short as possible.

### Test Cases

These are formatted as $$\K\$$, $$\S\$$ -> (expected output)

[qwertyuiop, asdfghjkl, zxcvbnm], qazg -> true
[qwertyuiop, asdfghjkl, zxcvbnm], qpazg -> false
[p, asdfghjklqwertyuio, zxcvbnm], sxzklkl -> true
[p, asdfghjklqwertyuio, zxcvbnm], b -> false
[qazwsxplkm, edocrfv, tgbyhnuji], bad -> true
[qazwsxplkm, edocrfv, tgbyhnuji], tex -> false
[thequick, brownfx, jmpsvlazydg], brow -> true
[thequick, brownfx, jmpsvlazydg], fox -> false

• qazwsxplkm, edcrfv, tgbyhnuji doesn't contains letter o? So will there be a testcase, like, [qazwsxplkm, edcrfv, tgbyhnuji], zoo?
– tsh
May 11, 2020 at 4:59
• I just typed this entire comment with my left hand. :) May 11, 2020 at 13:52
• "solely of letters appearing in the first half of each row". So, unlike in real life (first 5 characters), the Example1, "zxcvbnm", means that the letter "b" is not a left-hand letter. May 11, 2020 at 15:30
• Relevant XKCD
– Mark
May 12, 2020 at 0:00
• @Kaddath Keyboard with more rows: en.wikipedia.org/wiki/…
– tsh
May 12, 2020 at 1:50

# Python 3, 48 bytes

Takes as input $$\ K \$$ and $$\ S \$$, the keyboard and the target string. $$\ K \$$ is a taken as a list of lists.

lambda K,S:{r.pop(0)for r in K for i in r}>={*S}


Try it online!

## Explanation

It essentially converts $$\ S \$$ and the valid keyboard characters $$\ K' \$$ into sets, and returns True iff $$\ S \$$ is a subset of $$\ K' \$$. To obtain only the first half of each row of $$\ K \$$, we use the pop trick, which is explained in this answer of mine.

The previous answer, where $$\ K \$$ is taken as a list of strings instead.

# Python 3, 54 bytes

lambda K,S:{*''.join(r[:-~len(r)//2]for r in K)}>={*S}


Try it online!

# J, 22 21 bytes

a:=(-.>.@-:@#$])~&.>/  Try it online! -1 byte thanks to FrownyFrog Takes input as boxed words, with the string to test at the end. Reduces the list, set-subtracting -. the first half -.>.@-:@# of each row $] from the string to test.

Check if the result is empty: a:=

• Here also {. can be $May 11, 2020 at 6:55 # 05AB1E, 8 bytes -2 thanks to Kevin Cruijssen. ε2ä¬}JÃQ  Try it online! # Explanation ε For each item of the input list: 2ä Split into chunks of size 2 (conveniently, the middle character is included in the left half) ¬} Take the head of this list J Join the output string Ã List intersection with the input Q is the input  • -2 by using ÃQ instead of s€åß May 11, 2020 at 7:18 # Bash + Unix utilities, 57 bytes s()(echo${1:0:(${#1}+1)/2});grep ^[s$1s $2s$3]*$ Try it online! The three keyboard rows are passed as arguments, and the input string is passed on stdin. Output is the exit code (0 for truthy, 1 for falsey). This can probably be improved with clever golfing. # Jelly, 9 bytes ŒH€ZḢFfƑ@  A dyadic Link accepting a list of lists of characters on the left and a list of characters on the right which yields 1 if the right may be typed with the left hand or 0 if not. Try it online! ### How? ŒH€ZḢFfƑ@ - Link: keys, word ŒH€ - halve each Z - transpose Ḣ - head F - flatten @ - with swapped arguments: Ƒ - is invariant under?: f - filter keep  # Retina 0.8.2, 42 34 bytes r(?<-1>.)+(?<=(\w\w)+), , D\w ,$


Try it online! Link includes test cases. Explanation:

r(?<-1>.)+(?<=(\w\w)+),
,


The r modifier causes the regex to be processed from right to left, so that the , is matched first, before the lookbehind then matches as many pairs of letters as possible. .NET captures each matching pair into a stack of values for capture group 1. The balancing group then matches once for each value in the stack, thus deleting 1 character from the end of the row for every pair of letters.

D\w


Remove all duplicate letters.

,$ Check that no letters of S remain. # C (GCC) 114109108105 102 bytes -3 bytes ceilingcat i,h,t,r;f(char**k,char*s){for(r=1;*s;++s)for(i=3;i--;)for(h=strlen(k[i]);t=k[i][++h/2];)r*=t!=*s;i=r;}  Try it online! # Google Sheets, 106 99 Bytes =ArrayFormula(1-IsErr(Or(Find(Mid(B1,Row(Offset(A1,0,0,Len(B1))),1),Join(,Left(A:A,Round(Len(A:A)/2  Keyboard input is in the range A1:A3. The word is input in B1. After entering the formula, Sheets will automatically add 7 trailing parentheses. Join(,Left(A:A,Round(Len(A:A)/2))) combines the left-hand side of the keyboard rows into a single string we can search later. Mid(B1,Row(Offset(A1,0,0,Len(B1))),1) pulls each letter of the word, one character at a time. Or(Find(Mid(~),Join(~))) searches for each character in the search string and returns the location of each (a positive integer). If it can't find the character, it returns the #VALUE! error. Therefore, Or(~) returns either TRUE or #VALUE!. 1-IsErr(Or(~)) returns 1 for TRUE and 0 for #VALUE!. ArrayFormula(~) makes all these pieces work on input and output arrays instead of individual cells. This only matters for joining the input keyboard into a string and pulling out one character at a time from the word. # JavaScript (Node.js), 51 bytes k=>s=>s.every(c=>k.some(l=>l.length/l.search(c)>2))  Try it online! Input keyboard as an array of three strings. Input the word as an array of characters. # x86 32-bit machine code: 34 31 bytes Strategy: build a bitmap of left-hand keys with bit test-and-set (bts), then loop over the input string and check against that bitmap (bt). 386 bt* instructions with a register destination mask the bit-index like shifts do, so we can use ASCII characters directly as bit indices. (0x61 .. 0x7a) Inputs: • EDI: const char *str (0-terminated C string) • ESI: points at rows serialized into 3 back-to-back explicit-length strings in a buffer. 3x { dword length, char[] }, no indirection just flat byte stream concatenated to be loaded with lodsd / lodsb. Asm wrapper to do this conversion from an array of 3x char* included with the test harness on TIO. Output: AL=first non-left-hand character (non-zero=falsy), or zero=truthy (the terminator). Assembly language can just as easily jump on non-zero as on zero so this is justified. An alternate version of this took an explicit-length string and returned 0 or 1 in the CF flag. Clobbers: all GP registers except ESP  1 tlh: 2 00000000 31DB xor ebx, ebx ; left hand keys bitmap 3 00000002 8D4B03 lea ecx, [ebx + 3] 4 .rows: 5 00000005 AD lodsd ; length. Upper bytes zero because each letter can appear at most once. 6 00000006 8D2C06 lea ebp, [esi + eax] 7 00000009 92 xchg edx, eax 8 .keys: ; do{ 9 0000000A AC lodsb 10 0000000B 0FABC3 bts ebx, eax ; bmap |= 1<<(c & 31) 11 0000000E 4A dec edx 12 0000000F 4A dec edx 13 00000010 7FF8 jg .keys ; while(len-=2 > 0); // handles the len=0 case where --len becomes negative 14 00000012 89EE mov esi, ebp ; skip 2nd half of string 15 00000014 E2EF loop .rows 16 17 ;;; If the input string indexes any bit in EBX that isn't set, it's not left-hand typeable 18 00000016 89FE mov esi, edi 19 .chars: ; do{ 20 00000018 AC lodsb 21 00000019 0FA3C3 bt ebx, eax 22 0000001C 72FA jc .chars 23 .exit: 24 ; non-zero AL means we found a non-left-hand character. 25 ; zero means we found the terminating 0. ASCII 'a' = 0x61 so our bitmap always has bit 0 = 0 26 0000001E C3 ret  TODO: • A better row input format to save instructions in the input code? (When I designed this format, I forgot about having to skip over the right half of a row that I wasn't going to read. lea ebp, [start + len] + mov was a bugfix. It would be nice to at least not need that many regs.) • Maybe start with an all-1 bitmap and clear bits for right-hand keys? But that introduces a corner case for a row of length 1 which has no right-hand keys. Otherwise it might allow efficient looping to the end of a 0-terminated string. I used the nice C test harness from @Noodle9's answer. This is 32-bit asm so it would be a double pain to get something runnable on TIO.run with NASM. (You can use FASM to make a 32-bit executable directly, without a separate linker invocation, but I think that doesn't link libc. You could I guess inline a simple strlen, or hard-code some data structures to at least exit with a 0 / 1 exit status...) I put the source + asm wrapper (to adapt to a normal C calling convention), and C test harness itself on TIO even though you can't actually try it there. $ nasm -f elf32 -l/dev/stdout type-left-hand.asm &&
gcc -Wall -fno-pie -no-pie -m32 type-left-hand.[co] &&
./a.out
qwertyuiop asdfghjkl zxcvbnm
qazg -> 1 (1)
qpazg -> 0 (1)
p asdfghjklqwertyuio zxcvbnm
sxzklkl -> 1 (1)
b -> 0 (1)
qazwsxplkm edocrfv tgbyhnuji
tex -> 0 (1)
thequick brownfx jmpsvlazydg
brow -> 1 (1)
fox -> 0 (1)


0 / 1 (1) means left-hand typeable (matches the correct result)

# C (gcc), 116 $$\\cdots\$$ 108 104 bytes

Saved 4 bytes thanks to rtpax!!!
Saved 4 bytes thanks to ceilingcat!!!

c;p;l;r;i;f(char**k,char*s){for(r=1;*s;++s)for(i=3;i--;r&=l>p*2)for(l=p=0;c=k[i][l];++l)p=c-*s?p:l;c=r;}


Try it online!

Takes the keyboard as an array of strings and the word as a string.
Returns $$\1\$$ for true and $$\0\$$ otherwise.

• 112 brings you shorter than mine May 11, 2020 at 12:23
• @rtpax Going the other way with i - nice one, thanks! :-) May 11, 2020 at 12:45

x l=take(length ldiv2)l;l k s=all(\c->elem c$foldl(++)[]$map x k)s


Use (in ghci):

$let kbd = ["qwertyuiop","asdfghjkl;","zxcvbnm,./"]$ l kbd "stewardesses"
=> True
$l kbd "joker" => False  • foldl(++)[] is the same as concat. concat$map is the same as concatMap. Finally, concatMap x k is the same as just x=<<k. Jun 18, 2020 at 7:37
• l k s=all(...)s can be eta-reduced to l k=all(...). (\c->elem c$x=<<k) can be shortened to (elem(x=<<k)). Jun 18, 2020 at 7:40 • Using pointfree.io, it turns out x l=take(length ldiv2)l can be equivalently written as take=<<(div2).length and thus be inlined in l. Jun 18, 2020 at 7:44 • Using pointfree.io yet again, we arrive at a 44 byte version: all.flip elem.((take =<<(div2).length)=<<). Try it online! Jun 18, 2020 at 7:47 # C# (Visual C# Interactive Compiler), 56 63 bytes k=>s=>s.All(c=>k.Any(l=>(uint)l.IndexOf(c)/(float)l.Length<.5))  Try it online! • I think you need to include the usings in your byte count. In this case, I think you can inline them though May 15, 2020 at 3:18 • Admittedly, I don't really golf in C#, so there might be a shorter way than what I'm suggesting. I'd recommend checking out Tips for golfing in C# if you haven't already May 15, 2020 at 3:19 • Thanks for the tips! But if I can't use the usings I think I'll just write their keywords instead of the full types 😊 May 15, 2020 at 13:15 # R, 72 71 bytes function(k,s)all(t(sapply(k,function(x)match(s,x,0)))<(lengths(k)+2)/2)  Try it online! # JavaScript (ES6), 53 bytes Takes input as (keyboard)(word). Returns a Boolean value. k=>w=>!w.match([${k.map(r=>r.slice(-r.length/2))}])


Try it online!

# Charcoal, 12 bytes

⬤η⊙θ№…λ⊘⊕Ｌλι


Try it online! Link is to verbose version of code. Takes input as a list and a string. Output is a Charcoal boolean, i.e. - for true, nothing for false. Explanation:

 η              Input S
⬤               All characters
θ            Input K
⊙             Any row
λ         Current row
…          Truncated to
⊘⊕Ｌλ     Half its length rounded up
№      ι    Contains input character
Implicitly print


# Python 3, 53 bytes

lambda k,w:all(max(map(r.find,w))<len(r)/2for r in k)


Try it online!

# Python 2, 57 55 bytes

-2 bytes thanks to @ovs !

lambda k,s:all(t.find(c)*2<len(t)for t in k for c in s)


Try it online!

• 2 bytes shorter with t.find(c)*2<len(t).
– ovs
May 11, 2020 at 7:04