The Challenge

The goal of this challenge is to determine whether a given String can be typed using only one line of a standard UK QWERTY keyboard.

This is code golf, so shortest solution in bytes wins!


IO

Input will be a single String of zero or more characters in the ASCII decimal range of 32-126 inclusive. You may assume for this challenge that an empty String requires no typing and thus can be typed using a single line.

You may take the input as a String, list of characters, or equivalent form for your language.

Output should be a truthy value for any String that can be typed using a single line, or falsey for one that cannot.


Keyboard layout

To clarify any ambiguity over what the standard keyboard layout is below is a list of keys available on each line, including alternate upper keys (accessed using shift).

  • Line 1
    • Standard: `1234567890-=
  • Line 2
    • Standard: qwertyuiop[]
  • Line 3
    • Standard: asdfghjkl;'#
    • Uppercase: ASDFGHJKL
    • Special: Caps Lock
  • Line 4
    • Standard: \zxcvbnm,./
    • Alternate: |<>?
    • Uppercase: ZXCVBNM
    • Special: Shift
  • Line 5
    • Special: Space Bar

Alternate upper keys can only be pressed if Shift is also on the same line, and uppercase keys can only be accessed through Caps Lock or Shift. You really can only use one keyboard line!


Test cases

            -> true     (empty string)
45-2=43     -> true     (line 1)
qwerty      -> true     (line 2)
tryitout    -> true     (line 2)
Qwerty      -> false    (no shift or caps on line 2)
#sad        -> true     (line 3)
AsDf        -> true     (caps lock used)
@sDF        -> false    (no shift for alternate upper)
zxcvbn?     -> true     (line 4)
zxc vbn     -> false    (spacebar on separate line)
123abc      -> false    (multiple lines)
            -> true     (just space bar)
!!!         -> false    (exclamation marks cannot be printed by a single line)
  • So this is strictly U.S. keyboard layouts? (I've got a UK layout). – ouflak Oct 10 at 8:40
  • 2
    @ouflak On the contrary, it's strictly only the UK QWERTY keyboard layout – Luke Stevens Oct 10 at 8:42
  • @Arnauld Yes I did, thanks for noticing! – Luke Stevens Oct 10 at 8:43
  • Yeah I started looking at both and noticed your layout seems to follow the my UK keyboard layout, not my U.S. one. Hmmm... Wonder what my Austrian one looks like in comparison. – ouflak Oct 10 at 8:43
  • Are we allowed to take the input as a list of characters, or does it have to be a string? – Kevin Cruijssen Oct 10 at 8:48

15 Answers 15

Python 2, 130 123 121 115 bytes

lambda s:any(set(s)<=set(l+l.lower())for l in["`1234567890-=","eqwrtyuiop[]","ASDFGHJKL;'#","ZXCVBNM\,./|<>?"," "])

Try it online!


Python 3, 111 bytes

lambda s:any({*s}<={*l+l.lower()}for l in["`1234567890-=","eqwrtyuiop[]","ASDFGHJKL;'#","ZXCVBNM\,./|<>?"," "])

Try it online!

-4 bytes, thanks to nedla2004

  • 1
    If you are willing to use Python 3, you can get it down to 111 bytes. – nedla2004 Oct 10 at 14:07

Retina 0.8.2, 72 71 bytes

`^([-=\d`]+|[][eio-rtuwy]+|(?i)[adfghjkls;'#]+|[\\bcnmvxz,./|<>?]+| *)$

Try it online! Explanation: Each alternation matches a different row of the keyboard. The (?i) in the middle of the pattern causes the entire rest of the pattern to be matched case-insensitively. Edit: Saved 1 byte thanks to @KirillL.

  • opqr can be changed to o-r for -1. Also, did you forget a backtick character for Line 1, I don't see it? – Kirill L. Oct 10 at 9:33
  • @KirillL. Whoops, must have accidentally missed selecting it when copy/pasting, thanks for spotting that. – Neil Oct 10 at 9:37
  • nice [][... :) – mazzy Oct 10 at 12:18

05AB1E, 66 47 bytes

žh…`-=«žS„[]«žTDu…;'#««žUDu"\,./|<>?"««ð)εISåP}O

Takes the input as a list of characters.

-19 bytes thanks to @Emigna. Completely forgot we had qwerty-keyboard constant builtins. :D

Try it online or verify all test cases.

Explanation:

žh                # Push "0123456789"
  …`-=            # Push "`-="
      «           # Merge them together
žS                # Push "qwertyuiop"
  „[]             # Push "[]"
     «            # Merge them togeter
žT                # Push "asdfghjkl"
  Du              # Duplicate, and toUppercase the copy
    …;'#          # Push ";'#"
        ««        # Merge all three together
žU                # Push "zxcvbnm"
  Du              # Duplicate, and toUppercase the copy
    "\,./|<>?"    # Push "\,./|<>?"
              ««  # Merge all three together
ð                 # Push a space
 )                # Wrap all string in an array
  ε   }           # Map each to:
   I              #  Take the input (list of characters)
    å             #  Check for each if it's in the current string of the map-iteration
     P            #  Take the product (1 if all are truthy, 0 otherwise)
       O          # Take the sum (either 1 or 0, so truthy/falsey), and output implicitly

Perl 5 -pl, 76 bytes

$_=/^( *|[\d`=-]+|[][wetyuio-r]+|(?i)[asdfghjkl;'#]+|[\\\/zxcvbnm,.|<>?]+)$/

Try it online!

The obvious regex approach.

Ruby -n, 86 81 bytes

p /^( *|[\d`=-]+|[\]\[wetyuio-r]+|(?i)[asdfghjkl;'#]+|[\\\/zxcvbnm,.|<>?]+)$/?1:0

Try it online!

JavaScript (Node.js), 99 98 95 bytes

x=>/^([-`=\d]+|[wetyuio-r[\]]+)$/.test(x)|/^([asdfghjkl;'#]+|[zxcvbnm,./<>?\\|]+| *)$/i.test(x)

Try it online!

-1 from the comment by @Kirill L. in the Retina answer.
-3 thanks @Ismael Miguel and @Arnauld for their combined effort.

  • Why do you have asdfghjkl and ASDFGHJKL in there? Why don't you use the i flag, like x=>/.../i.test(x)? – Ismael Miguel Oct 10 at 9:34
  • @IsmaelMiguel because QWERTYUIOP shouldn't be matched by the regex. The extra check will cost more than coding those uppercase letters directly into the regex. – Shieru Asakoto Oct 10 at 9:35
  • Nevermind, it is buggy. x=>/^([-`=\d]+|[wetyuio-r\[\]]+)$/.test(x)||/^([asdfghjkl;'#]+|[zxcvbnm,.\/<>?\\|]+| *)$/i.test(x) is the same length – Ismael Miguel Oct 10 at 9:38
  • @IsmaelMiguel Using a bitwise | saves 1 byte. – Arnauld Oct 10 at 9:41
  • 1
    @IsmaelMiguel This is part of the ECMAScript specification. The only 3 characters that need to be escaped within a character class are \ , ] and - (see ClassAtomNoDash in the spec). The dash may also appear unescaped if it's either the first or the last character (otherwise, it's interpreted as a character range separator). – Arnauld Oct 10 at 13:27

Perl 6, 102 101 100 bytes

-1 byte thanks to nwellnhof!

->\a{max map {a.comb⊆comb $_~.lc:},|<eqwrtyuiop[] ASDFGHJKL;'# ZXCVBNM\,./|<>?>,' ',"`-="~[~] ^10}

Try it online!

Pretty standard implementation. There's probably a shorter regex based solution to this, but I don't know Perl 6's regex well enough to make an attempt.

  • You can use max instead of ?any (and min instead of ?all). – nwellnhof Oct 10 at 13:03
  • 1
    For what it's worth, a regex-based solution would be 84 bytes or 80 bytes using Perl 5 regexes with m:P5//. But I don't think either of them are worth posting as answer. – nwellnhof Oct 10 at 13:41

Java 10, 209 208 bytes

s->{int l=0,t;for(var p:s){t=p.matches("[[0-9]`\\-=]")?1:"qwertyuiop[]".contains(p)?2:p.matches("(?i)[asdfghjkl;'#]")?3:"\\zxcvbnm,./|<>?ZXCVBNM".contains(p)?4:p.equals(" ")?5:9;l=l<1?t:l!=t?9:l;}return l<6;}

-1 byte thanks to @TFeld.

Try it online.

Explanation:

s->{                    // Method with String-array parameter and boolean return-type
  int l=0,              //  Line-integer, starting at 0
      t;                //  Temp integer
  for(var p:s){         //  Loop over the characters
    t=p.matches("[[0-9]`\\-=]")?
                        //   If it's a character from the first line: 
       1                //    Set `t` to 1
      :"qwertyuiop[]".contains(p)?
                        //   Else-if it's a character from the second line:
       2                //    Set `t` to 2
      :p.matches("(?i)[asdfghjkl;'#]")?
                        //   Else-if it's a character from the third line
       3                //    Set `t` to 3
      :"\\zxcvbnm,./|<>?ZXCVBNM".contains(p)?
                        //   Else-if it's a character from the fourth line:
       4                //    Set `t` to 4
      :p.equals(" ")?   //   Else-if it's a space from the fifth line:
       5                //    Set `t` to 5
      :                 //   Else (invalid character):
       9;               //    Set `t` to 9
    l=l<1?              //   If `l` is still 0:
       t                //    Set it to `t`
      :l!=t?            //   Else-if `t` is a different line than `l`:
       9                //    Set `l` to 9 (non-existing line)
      :                 //   Else (`t` is the same line as `l`):
       l;}              //    Leave `l` the same
  return l<6;}          //  Return whether `l` is not 9
  • Nice solution, but it returns true for !!! despite exclamation marks being unable to be printed by any single line. I've just added that as a test case – Luke Stevens Oct 10 at 9:21
  • @LukeStevens Ah, I falsely assumed the input is always valid for one of the five lines. I will modify my solution. It's a simple fix (but will add some bytes..) – Kevin Cruijssen Oct 10 at 9:38
  • Why not use (?i) for the other uppercase letters? – Neil Oct 10 at 9:43
  • @Neil Because of the escape required for the slash, it's also 209 bytes. – Kevin Cruijssen Oct 10 at 9:46

Powershell, 87 bytes

Port of Neil's Retina regex.

"$args"-cmatch"^([-=\d``]+|[][eio-rtuwy]+|(?i)[adfghjkls;'#]+|[\\bcnmvxz,./|<>?]+| *)$"

Jelly, 55 bytes

ØDW;Øq;Œu$€3,4¦;"“rɠ3“Ż²“¿µ|“aƲƘ0ÞḅzḂ»⁶ṭ
¢f€ẈṠSỊafƑ¢F¤$

Try it online!

The first line yields a list of the keyboard lines and the second one checks whether the program input is contained on a single (or zero) lines and that there are no characters that can't be typed (like QWE!@#).

C, 150 bytes

x;f(char*s){for(x=0;*s;x|="@               "[*s++-32]);return!(x&x-1);}

Try it online!

It won't win any prizes, but this is a fun approach: we map characters on the five rows of the keyboard to 4 8 16 32 64, and every invalid character to 3. We bitwise OR the value for each character in the string together, and check if the result satisfies x&(x-1) == 0, which is true when x is a power of 2 or zero, i.e. whenever x has at most one bit set.

LUA, 282 262 259 270 bytes

s=io.read()l=0
t=0
for c in s:gmatch"."do
f=c.find
t=f(c,"[0-9%`-=]")or 0|(f(c,"[qwertyuiop%[%]]")or 0)*2|(f(c,"[aAsSdDfFgGhHjJkKlL:'@#~]")or 0)*4|(f(c,"[\\zxcvbnm,./|<>?ZXCVBNM]")or 0)*8|(f(c," ")or 0)*16
t=t==0 and 17or t
l=l<1 and t or l~=t and 17or l
end
print(l<17)

Try it online!

PHP, 98 bytes

I´m a little sad that there´s nothing shorter than regex. It´s probably not the fastest solution.

<?=preg_match("%^([`\d=-]*|[wetyuio-r[\]]*|(?i)[asdfghjkl;'#]*|[\\\zxcvbnm,./|<>?]*| *)$%",$argn);

Run as pipe with -F or try it online.


Shortest non-regex solution I found (124 bytes; linebreak and tab for reading convenience):

foreach(["`1234567890-=","qwertyuiop[]","asdfghjkl;'#ASDFGHJKL","zxcvbnm,./\|<>?ZXCVBNM"," "]as$d)
    trim($argn,$d)>""||die(1);

exits with code 1 for truthy, 0 for falsy. Run as pipe with -R.
Requires PHP 5.4 or later; for older PHP, use array(...) instead of [...] (+5 bytes)
or use these 123 bytes with -nR in PHP <7:

foreach(split(_,"`1234567890-=_qwertyuiop[]_asdfghjkl;'#ASDFGHJKL_zxcvbnm,./\|<>?ZXCVBNM_ ")as$d)
    trim($argn,$d)>""||die(1);

AWK, 163 119 113 bytes

This is an AWK answer, returns a numeric 1 string for true, 0 string for false. (Written as AWK invoke as awk -f file for interactive use.)

{print/^[-`1234567890=]*$/||/^[]qwertyuiop\[]*$/||/^[asdfghjkl;'#ASDFGHJKL]*$/||/^[zxcvbnm,.\/\|<>?ZXCVBNM]*$/||/^ *$/}

Try it online!

However, does not handle TAB character as written (trivial extension) as not part of spec.

  • 4
    "All solutions to challenges should: (…) Be a serious contender for the winning criteria in use. For example, an entry to a code golf contest needs to be golfed, and an entry to a speed contest should make some attempt to be fast." – help center There is no need to declare those 2 variables and definitely no need to make them strings. And a single print is enough: Try it online! – manatwork Oct 15 at 11:57
  • Your comment that solutions should be serious is valid but I would argue that any solution therefore after the revised 05AB1E one is therefore irrelevant since they could not match or better the result. I don't see how you can not use print twice to get both true and false (certainly for something that reads strings until end of input). And I did say optimized not optimal at the time. Thanks to your thoughts it can be reduced to 143 characters. – Phil F Oct 15 at 12:34
  • And in fact going all the way will make it 121 characters ... – Phil F Oct 15 at 12:44
  • 2
    code-golf is not a competition for shortest code overall, but shortest for your language. If you can improve your solution, then I'd suggest doing so – Jo King Oct 15 at 12:57
  • @Jo King, thank you for your clarification(s). revised code would/could be: {print/^[-```1234567890=]*$/||/^[]qwertyuiop\[]*$/||/^[asdfghjkl;'#ASDFGHJKL]*$/||/^[zxcvbnm,.\/\|<>?ZXCVBNM]*$/||/^ *$/} – Phil F Oct 15 at 13:16

Bash, 119 bytes

Includes an echo to provide "readable" output. If you put a suitable wrapper of shell (your choice) around it to include print/output, then you can save 8 bytes. My reading of the challenge suggests the solution should output a suitable output indication so I stick with 119 bytes.

[[ "$@" =~ ^[asdfghjklASDFGHJKL\;\'#]*$|^[-\`0-9=]+$|^[]qwertyuiop\[]*$|^[zxcvbnm,./\|\<\>\?ZXCVBNM]*$|^\ *$ ]];echo $?

Try it online!

  • Be advised the Bash solution also means my/@manatwork AWK solution may be reduced to 113 bytes. – Phil F Oct 19 at 10:03

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.