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 code-golf. 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
18 Answers 18
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}
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}
J, 22 21 bytes
a:=(-.>.@-:@#$])~&.>/
-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:=
-
1\$\begingroup\$ Here also {. can be $ \$\endgroup\$FrownyFrog– FrownyFrog2020年05月11日 06:55:31 +00:00Commented May 11, 2020 at 6:55
05AB1E, 8 bytes
-2 thanks to Kevin Cruijssen.
ε2ä¬}JÃQ
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
-
1\$\begingroup\$ -2 by using
ÃQinstead ofs€åß\$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2020年05月11日 07:18:15 +00:00Commented May 11, 2020 at 7:18
Bash + Unix utilities, 57 bytes
s()(echo ${1:0:(${#1}+1)/2});grep ^[`s 1ドル``s 2ドル``s 3ドル`]*$
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.
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) (削除) 114 (削除ここまで) (削除) 109 (削除ここまで) (削除) 108 (削除ここまで) (削除) 105 (削除ここまで) 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;}
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))
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]+movwas 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
bad -> 1 (1)
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;}
Takes the keyboard as an array of strings and the word as a string.
Returns \1ドル\$ for true and \0ドル\$ otherwise.
-
1
-
\$\begingroup\$ @rtpax Going the other way with
i- nice one, thanks! :-) \$\endgroup\$Noodle9– Noodle92020年05月11日 12:45:46 +00:00Commented May 11, 2020 at 12:45
Haskell, 69 bytes
x l=take(length l`div`2)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
-
\$\begingroup\$
foldl(++)[]is the same asconcat.concat$mapis the same asconcatMap. Finally,concatMap x kis the same as justx=<<k. \$\endgroup\$Laikoni– Laikoni2020年06月18日 07:37:25 +00:00Commented Jun 18, 2020 at 7:37 -
\$\begingroup\$
l k s=all(...)scan be eta-reduced tol k=all(...).(\c->elem c$x=<<k)can be shortened to(`elem`(x=<<k)). \$\endgroup\$Laikoni– Laikoni2020年06月18日 07:40:27 +00:00Commented Jun 18, 2020 at 7:40 -
\$\begingroup\$ Using pointfree.io, it turns out
x l=take(length ldiv2)lcan be equivalently written astake=<<(`div`2).lengthand thus be inlined inl. \$\endgroup\$Laikoni– Laikoni2020年06月18日 07:44:13 +00:00Commented Jun 18, 2020 at 7:44 -
\$\begingroup\$ Using pointfree.io yet again, we arrive at a 44 byte version:
all.flip elem.((take =<<(`div`2).length)=<<). Try it online! \$\endgroup\$Laikoni– Laikoni2020年06月18日 07:47:36 +00:00Commented 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))
-
\$\begingroup\$ I think you need to include the
usings in your byte count. In this case, I think you can inline them though \$\endgroup\$math junkie– math junkie2020年05月15日 03:18:31 +00:00Commented May 15, 2020 at 3:18 -
\$\begingroup\$ 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 \$\endgroup\$math junkie– math junkie2020年05月15日 03:19:43 +00:00Commented May 15, 2020 at 3:19
-
\$\begingroup\$ 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 😊 \$\endgroup\$Netråm– Netråm2020年05月15日 13:15:30 +00:00Commented 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)
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))}]`)
Charcoal, 12 bytes
⬤η⊙θNo...λ⊘⊕Lλι
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
⊘⊕Lλ Half its length rounded up
No ι Contains input character
Implicitly print
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)
-
2\$\begingroup\$ 2 bytes shorter with
t.find(c)*2<len(t). \$\endgroup\$ovs– ovs2020年05月11日 07:04:52 +00:00Commented May 11, 2020 at 7:04
qazwsxplkm, edcrfv, tgbyhnujidoesn't contains lettero? So will there be a testcase, like,[qazwsxplkm, edcrfv, tgbyhnuji], zoo? \$\endgroup\$