33
\$\begingroup\$

The three rows of the qwerty keyboard are qwertyuiop, asdfghjkl and zxcvbnm. Your task is to find the longest word that can be typed using only one row of the keyboard, from a given list of words.

Sample input 1

artist
home
gas
writer
geology
marine
twerp

Output

writer

(Of the given words, only gas, writer and twerp can be written using a single row, and writer is the longest)

The words may not be actual words (so don't assume the third row to be invalid). However, you can assume that there will always be exactly one answer (no more, no less).

Sample input 2

wrhuji
bxnzmmx
gllwssjjd
vnccbb
lrkjhgfdsa
tttttt

Output

bxnzmmx

Additional punctuation and whitespaces can be provided in input (as per language requirements). However, no extra output should be given. Input and output are in lower case. Shortest code wins.

\$\endgroup\$
11
  • \$\begingroup\$ @MartinBüttner I would really like to see how this could be solved with Retina. Do you think it is easily done? \$\endgroup\$ Mar 17, 2016 at 5:51
  • \$\begingroup\$ Are the input words always in lowercase? \$\endgroup\$
    – nimi
    Mar 17, 2016 at 5:59
  • \$\begingroup\$ @nimi Yes, they are. \$\endgroup\$ Mar 17, 2016 at 6:20
  • \$\begingroup\$ @ghosts_in_the_code You should state that in the challenge for clarity \$\endgroup\$
    – Luis Mendo
    Mar 17, 2016 at 9:00
  • 8
    \$\begingroup\$ Relevant xkcd: what-if.xkcd.com/75 \$\endgroup\$ Mar 17, 2016 at 17:35

19 Answers 19

19
\$\begingroup\$

Python 2, 84 bytes

lambda l:max(l,key=lambda w:(-len({"asdfghjklzxcvbnm".find(c)/9for c in w}),len(w)))

Finds the max of the input, comparing by fewer keyboard rows spanned, then in increasing length. The keyboard row value is extracted by "asdfghjklzxcvbnm".find(c)/9, which takes the middle row to 0, the bottom row to 1, and the top row, which is excluded, to -1, since find gives -1 for missing values.

Other attempts:

lambda l:max((-len({"asdfghjklzxcvbnm".find(c)/9for c in w}),len(w),w)for w in l)[2]
lambda l:max(l,key=lambda w:len(w)-1./len({"asdfghjklzxcvbnm".find(c)/9for c in w}))
lambda l:max([w for w in l if len({"asdfghjklzxcvbnm".find(c)/9for c in w})<2],key=len)
\$\endgroup\$
6
  • 1
    \$\begingroup\$ 1 / character more for Python 3 ;) \$\endgroup\$ Mar 17, 2016 at 14:17
  • 7
    \$\begingroup\$ I didn't know the grammar allowed for no whitespace between 9 and for... \$\endgroup\$
    – jogloran
    Mar 18, 2016 at 2:09
  • \$\begingroup\$ This is by far my favorite answer. \$\endgroup\$
    – SBI
    Mar 18, 2016 at 14:12
  • 2
    \$\begingroup\$ @jogloran, so long as the characther is not an E or e, you can pretty much always remove whitespace between a number and a variable name/keyword \$\endgroup\$
    – wnnmaw
    Mar 18, 2016 at 14:35
  • \$\begingroup\$ @wnnmaw That's no longer true for newer versions of Python, e.g. 4if 0else 2 is valid in 2.7.11 and 3.5.1 (and probably has been valid for a few versions prior) \$\endgroup\$
    – Sp3000
    Mar 21, 2016 at 5:10
13
\$\begingroup\$

Japt, 32 30 bytes

;Uf_¬£DbXu)f10Ãä¥ eÃn@Yl -XlÃg

Test it online! Input is an array of strings.

How it works

;Uf_  ¬ £  DbXu)f10Ã ä¥  eà n@  Yl -Xlà g
;UfZ{Zq mX{DbXu)f10} ä== e} nXY{Yl -Xl} g

         // Implicit: U = input array of strings
;        // Reset variables A-L to various values.
         // D is set to the string "QWERTYUIOP\nASDFGHJKL\nZXCVBNM".
UfZ{   } // Take U and filter to only the items Z that return truthily to this function:
Zq       //  Split Z into chars, then
mX{    } //  map each char X by this function:
DbXu)    //   Return D.indexOf(X.toUpperCase()),
f10      //   floored to a multiple of 10.
         //  This maps each char to 0 for the top row, 10 for the middle, 20 for the bottom.
q ä==    //  Split the resulting string into chars and check each pair for equality.
e        //  Check that every item in the result is truthy. This returns true if all chars
         //  are on the same row; false otherwise.
         // Now we have only the words that are entirely on one row.
nXY{   } // Sort by passing each two args X and Y into this function:
Yl -Xl   //  Return Y.length - X.length. Sorts the longest to the front.
g        // Get the first item in the resulting array. Implicitly output.
\$\endgroup\$
6
  • 8
    \$\begingroup\$ Woah, did you just outgolf Dennis? \$\endgroup\$ Mar 17, 2016 at 14:35
  • 1
    \$\begingroup\$ This is going to be hard to beat \$\endgroup\$
    – Adnan
    Mar 17, 2016 at 14:43
  • 2
    \$\begingroup\$ Do you have "QWERTYUIOP\nASDFGHJKL\nZXCVBNM"a a predefined literal? Well played :-) \$\endgroup\$
    – Luis Mendo
    Mar 17, 2016 at 14:58
  • 1
    \$\begingroup\$ I can't seem to find where it is stated that D is set to QWERTYUIOP\nASDFGHJKL\nZXCVBNM, even the page you refer to seems to state Variables <...> D 13 \$\endgroup\$
    – sukhmel
    Mar 17, 2016 at 15:54
  • 1
    \$\begingroup\$ @sukhmel A ; at the beginning of the program resets variables A-L to various values. D is set to the keyboard string. You can find more info here. \$\endgroup\$ Mar 17, 2016 at 16:11
11
\$\begingroup\$

Python 2.5+ and 3, 93 bytes

Had to test how many strokes for this approach; this uses the fact that a.strip(b) results in empty string if a solely consists of characters that occur in b.

The function takes list of strings and returns a string.

lambda a:max(a,key=lambda x:(~all(map(x.strip,['qwertyuiop','asdfghjkl','zxcvbnm'])),len(x)))
\$\endgroup\$
4
  • 5
    \$\begingroup\$ Welcome to PPCG, nice first post :) \$\endgroup\$ Mar 17, 2016 at 14:20
  • \$\begingroup\$ Why is the key variable there? I think you can remove it. \$\endgroup\$ Mar 17, 2016 at 19:58
  • \$\begingroup\$ @CatsAreFluffy no, that is not possible. the key argument of max function is keyword-only. \$\endgroup\$ Mar 17, 2016 at 20:16
  • \$\begingroup\$ Ack, I forgot about kwargs. \$\endgroup\$ Mar 17, 2016 at 20:21
8
\$\begingroup\$

Retina, 73 bytes

G`^([eio-rtuwy]+|[adfghjkls]+|[bcmnvxz]+)$
1!`(.)+(?!\D+(?<-1>.)+(?(1)!))

Try it online!

Conclusion: Retina needs a sorting stage.

Explanation

G`^([eio-rtuwy]+|[adfghjkls]+|[bcmnvxz]+)$

This is a grep stage: it only keeps lines which are matched by the regex. I.e. those which are formed exclusively from one of those character classes.

1!`(.)+(?!\D+(?<-1>.)+(?(1)!))

Now we just need to find the largest of the remaining strings. We do this by matching all words which are at least as long than all the words after them. The 1 is a new addition to Retina (released two days ago), which limits this match stage to considering only the first such match. And ! instructs Retina to print the match (instead of counting it).

\$\endgroup\$
6
  • \$\begingroup\$ This is cool! I'll have to look at balanced groups sometime. Although they seem to be hard to understand. I've tried this (and it looks like it works), but I wanted to wait for your answer to see what kind of thing will you create. \$\endgroup\$
    – daavko
    Mar 17, 2016 at 22:33
  • \$\begingroup\$ @daavko Sorry for the self-promotion, but have you read my Stack Overflow post on balancing groups? I'm being told it's a good introduction. The concept itself really isn't that complicated, especially if you don't use the (?<a-b>...) syntax which is rarely needed in code golf. \$\endgroup\$ Mar 17, 2016 at 22:46
  • \$\begingroup\$ I don't think I've seen that post. Most likely because I don't browse Stack Overflow often. Thanks for the link, I'll bookmark it and read it. \$\endgroup\$
    – daavko
    Mar 17, 2016 at 22:57
  • 1
    \$\begingroup\$ Probably off topic for a comment, but pretty much 100% of my knowledge of balancing groups comes from having read your post. I think you can self-promote it any time, it's a great help :) Anyway more on topic, would sort stage work something like <code>O-1`.*</code> to get the line with the longest input? Maybe it should work like transliteration and have more than one backtick separated region, a regex to split on and one to count? Actually maybe this also belongs in chat... ¯_(ツ)_/¯ \$\endgroup\$ Mar 18, 2016 at 3:26
  • \$\begingroup\$ Never would have thought of o-r, awesome. \$\endgroup\$ Mar 19, 2016 at 6:29
6
\$\begingroup\$

Java, 154 142 or 142 130 bytes

Because, ya know, Java.

C#, for comparison.

146 bytes if input has to be a single string with values separated by \n:

s->java.util.Arrays.stream(s.split("\n")).filter(g->g.matches("[wetyuio-r]*|[asdfghjkl]*|[zxcvbnm]*")).max((a,b)->a.length()-b.length()).get()

134 bytes if I can assume input as String[] instead:

s->java.util.Arrays.stream(s).filter(g->g.matches("[wetyuio-r]*|[asdfghjkl]*|[zxcvbnm]*")).max((a,b)->a.length()-b.length()).get()

Slightly ungolfed:

UnaryOperator<String> longestQwertyStr = s -> 
        java.util.Arrays.stream(s.split("\n")) // Split string input over `\n` and convert to Stream<String>
                .filter(g->g.matches("[wetyuio-r]*|[asdfghjkl]*|[zxcvbnm]*")) // Filter to Strings that only use characters from a single row
                .max((a,b)->a.length()-b.length()) // Find the max by comparing String length
                .get(); // Convert from Optional<String> to String and implicit return single statement lambda

Second lambda is a Function<String[],String>.

\$\endgroup\$
9
  • \$\begingroup\$ My updated version now even beats the shorter lambda as a full program :) \$\endgroup\$
    – SBI
    Mar 22, 2016 at 8:36
  • \$\begingroup\$ @SBI Curse Java's long method invocations! (All in good fun) \$\endgroup\$
    – CAD97
    Mar 22, 2016 at 14:12
  • \$\begingroup\$ Being a Java developer myself, it's nice to see a verbose language that manages to be concise at the same time :) \$\endgroup\$
    – SBI
    Mar 22, 2016 at 14:26
  • \$\begingroup\$ If we're going to make an input assumption, lets make the one that helps us the most, input comes in as a List<String>:l->l.stream().filter(g->g.matches("[wertyuio-r]*|[asdfghjkl]*|[zxcvbnm]*")).max((a,b)->a.length()-b.length()).get() (116 chars) \$\endgroup\$
    – Andreas
    Mar 23, 2016 at 21:29
  • \$\begingroup\$ @Andreas As per my recent meta discussion, if a lambda takes a List, you have to include import java.util.*; in the byte count, meaning taking a list is -16 bytes by taking a List but +19 to import the List. HOWEVER, you did catch there using max instead of reduce for a gain of -7 bytes. \$\endgroup\$
    – CAD97
    Mar 23, 2016 at 21:48
4
\$\begingroup\$

Jelly, 40 34 bytes

p“£vẈ¬ḣ“£AS°GƤg“£ḷḳƤ²ƤȤḤ»f/€fµL€Mị

Try it online!

How it works

p“£vẈ¬ḣ“£AS°GƤg“£ḷḳƤ²ƤȤḤ»f/€fµL€Mị

 “£vẈ¬ḣ“£AS°GƤg“£ḷḳƤ²ƤȤḤ»           Use dictionary compression to yield
                                    ['quipo twyer', 'adj flash jg', 'bcmnz xv'].
p                                   Cartesian product; for all pairs of an input
                                    string and one of the rows.
                         f/€        Reduce each pair by filter, keeping only the
                                    letters in the input string that are on that
                                    particular keyboard row.
                            f       Filter the results, keeping only filtered words
                                    that occur in the input.
                             µ      Begin a new chain.
                              L€    Get the length of each kept word.
                                M   Get the index corr. to the greatest length.
                                 ị  Retrieve the word at that index.
\$\endgroup\$
1
  • \$\begingroup\$ Why don't you use the QWERTY builtin Øq? Did it not exist when this answer was posted? \$\endgroup\$
    – xigoi
    Nov 2, 2020 at 20:48
4
\$\begingroup\$

Python 3, 98

Saved 5 bytes thanks to Kevin.
Saved 3 bytes thanks to PM 2Ring.
Saved 3 bytes thanks to Antti Haapala.

Brute forcing it at the moment. I filter the words down to only those contained by a single row, and then sort for max string length.

lambda a:max(a,key=lambda x:(any(map(set(x).__le__,['qwertyuiop','asdfghjkl','zxcvbnm'])),len(x)))

Test cases:

assert f(['asdf', 'qwe', 'qaz']) == 'asdf'
assert f('''artist
home
gas
writer
geology
marine
twerp'''.splitlines()) == 'writer'
assert f('''wrhuji
bxnzmmx
gllwssjjd
vnccbb
lrkjhgfdsa
tttttt'''.splitlines()) == 'bxnzmmx'
\$\endgroup\$
3
\$\begingroup\$

PowerShell v2+, 72 bytes

($args-match"^([qwertyuiop]+|[asdfghjkl]+|[zxcvbnm]+)$"|sort Length)[-1]

Takes input via command-line arguments as $args, then uses the -match operator with a regex to select only the words that are exclusively made up of one keyboard row. We pipe those results into Sort-Object that sorts by the property Length. We can do this since strings in PowerShell are all of the System.String type, which includes .Length as a sortable property. This sorts the strings into ascending order by length, so we take the last one with [-1], leave it on the pipeline, and output is implicit.

Example

PS C:\Tools\Scripts\golfing> .\longest-word-qwerty-keyboard.ps1 asdf qwe zxc typewriter halls establishment
typewriter
\$\endgroup\$
3
\$\begingroup\$

Pyth, 45 35 bytes

Thanks to @FryAmThe Eggman for saving me some bytes!

elDf}k-LTc."`z:I¿Ç  Ì(T4²ª$8·"\`Q

Try it here!

Takes input as a list of words.

Explanation

elDf}k-LTc."..."\`Q   # Q = list of all input words

   f              Q   # Filter input with T as lambda variable
         c."..."\`    # List of all keyboard rows
      -LT             # Remove all letters of the current input row from the current input
                      # word. Results in a list of 3 string with one being empty if
                      # the word can be typed with one row
    }k                # Check if the list contains an emtpy string
elD                   # order result list by length and take the last
\$\endgroup\$
0
3
\$\begingroup\$

Ruby, 88 82 69

If I'm not allowed to take a list of strings and must take a multiline string, add +12 to the score and add .split('\n') right before the .grep call.

Thanks CatsAreFluffy for teaching me about stabby lambdas in Ruby, and further optimizations from manatwork

->x{x.grep(/^([o-rwetyui]+|[asdfghjkl]+|[zxcvbnm]+)$/).max_by &:size}
\$\endgroup\$
4
  • \$\begingroup\$ No, you add .split('\n') before the .select, right? And why no stabby lambdas? \$\endgroup\$ Mar 17, 2016 at 17:28
  • \$\begingroup\$ I didn't know about the stabby lambda until just now, when you mentioned it. Thanks! \$\endgroup\$
    – Value Ink
    Mar 17, 2016 at 17:34
  • \$\begingroup\$ Can you add a space between the -88- and 82? \$\endgroup\$ Mar 17, 2016 at 19:56
  • \$\begingroup\$ No need to assign it to a variable, anonymous functions are allowed; if the only thing to do inside .select's code block is to match it against a regular expression, .grep is more suitable; no need to put parenthesis around the last method's parameters in a call chain; .length has a shorter alias, .size: ->x{x.grep(/^([o-rwetyui]+|[asdfghjkl]+|[zxcvbnm]+)$/).max_by &:size} \$\endgroup\$
    – manatwork
    Mar 18, 2016 at 7:48
3
\$\begingroup\$

awk , 92 84 81 bytes

(/^([wetyuio-r]+|[asdfghjkl]+|[zxcvbnm]+)$/)&&length>length(a){a=$0}END{print a}  

saved 3 bytes thanks to @Wolfgang suggestion

\$\endgroup\$
5
  • \$\begingroup\$ You can subtract a byte by using [wetyuio-r] instead, and also two more by doing /^(expr|expr|expr)$/ instead of `/^expr$|^expr$|^expr$/ \$\endgroup\$
    – Wolfgang
    Mar 19, 2016 at 19:35
  • \$\begingroup\$ @Wolfgang: thanks for the tip. I tried already the 2nd one and on my awk it gave me the longest word instead of the right one... I'll try again. I agree with the first one though, saves 1 byte \$\endgroup\$ Mar 21, 2016 at 4:42
  • \$\begingroup\$ @Wolfgang: hmm, does work this time (I may have fumbled the parenthesis on my own test, friday). I edit your tips in, thanks. \$\endgroup\$ Mar 21, 2016 at 4:45
  • \$\begingroup\$ Are you sure the parenthesis around the regular expression are necessary? gawk and mawk are happy without them. \$\endgroup\$
    – manatwork
    Mar 21, 2016 at 10:32
  • \$\begingroup\$ @manatwork: the inner ones are necessary, the outer one I prefer to have them to ensure the logic and evaluation order. .. maybe at the "cost" of 2 chars \$\endgroup\$ Mar 21, 2016 at 10:40
3
\$\begingroup\$

C#, 141 / 112 / (120 bytes)

Contender for worst golfing language, for obvious reasons. Uses "my" locale with qwertz instead of qwerty but works fine otherwise.

Full program without where:

static void Main(string[]a){Console.WriteLine(a.OrderBy(x=>x.Length).Last(x=>Regex.IsMatch(x,"^([qwertzuiop]+|[asdfghjkl]+|[yxcvbnm]+)$")));}

Only output without Where:

Console.WriteLine(a.OrderBy(x=>x.Length).Last(x=>Regex.IsMatch(x,"^([qwertzuiop]+|[asdfghjkl]+|[yxcvbnm]+)$")));

Only output (original):

Console.WriteLine(a.Where(x=>Regex.IsMatch(x,"^([qwertzuiop]+|[asdfghjkl]+|[yxcvbnm]+)$")).OrderBy(x=>x.Length).Last());
\$\endgroup\$
3
  • 1
    \$\begingroup\$ The challenge says you have to use querty and since you are hardcoding this anyway, I don't see any reason to change that. Also you can use a function (maybe even a lambda) instead of a full program to save some bytes. This is always allowed unless explicitly forbidden in the challenge. \$\endgroup\$
    – Denker
    Mar 18, 2016 at 14:01
  • \$\begingroup\$ Being proud of my layout :P, it's not like I have a chance at winning anyway using C#. Edited in a version that's only the output. \$\endgroup\$
    – SBI
    Mar 18, 2016 at 14:07
  • \$\begingroup\$ Added another alternative, using only Last with a predicate instead of using where. This shaves off another 8 bytes. \$\endgroup\$
    – SBI
    Mar 21, 2016 at 11:16
2
\$\begingroup\$

bash, 105 bytes

And various other utilities, of course.

egrep -x '[wetyuio-r]+|[asdfghjkl]+|[zxcvbnm]+'|awk '{print length($0)"\t"$0;}'|sort -n|cut -f2|tail -n1
\$\endgroup\$
1
  • \$\begingroup\$ The awk code can be written shorter as $0=length"\t"$0. \$\endgroup\$
    – manatwork
    Mar 21, 2016 at 10:39
1
\$\begingroup\$

MATL, 54 bytes

[]y"@Y:nh]2$SP"@Y:!t'asdfghjkl'mw'zxcvbnm'myy+~hhAa?@.

This works with current version (14.0.0) of the language/compiler.

Input format is (first example)

{'artist' 'home' 'gas' 'writer' 'geology' 'marine' 'twerp'}

or (second example)

{'wrhuji' 'bxnzmmx' 'gllwssjjd' 'vnccbb' 'lrkjhgfdsa' 'tttttt'}

Try it online!

Explanation

[]               % push empty array. Will be used for concatenation
y                % take input array implicitly at bottom of stack, and copy onto top
"                % for each string
  @Y:            %   push current string
  nh             %   get its length. Concatenate with array of previous lengths
]                % end for each
2$S              % sort the original copy of input array by increasing string length
P                % flip: sort by decreasing length
"                % for each string in decreasing order of length
  @Y:!           %   push that string as a column char vector
  t'asdfghjkl'm  %   duplicate. Tru for chars in 2nd row of keyboard
  w'zxcvbnm'm    %   swap. True for chars in 3rd row of keyboard
  yy+~           %   duplicate top two arrays, sum, negate: true for chars in 1st row
  hh             %   concatenate horizontally twice
  Aa             %   true if any column has all true values
  ?              %   if that's the case
    @            %     push string  
    .            %     break for each loop
                 %   end if implicitly
                 % end for each
                 % display implicitly
\$\endgroup\$
1
\$\begingroup\$

Perl, 81 bytes

$a=$1 if/^([wetyuio-r]+|[asdfghjkl]+|[zxcvbnm]+)$/&&1<<y///c>$a=~y///c;END{say$a}

Symbol to letter count pretty high.

\$\endgroup\$
0
\$\begingroup\$

Groovy, 65 characters

{it.grep(~/[o-rwetyui]+|[asdfghjkl]+|[zxcvbnm]+/).max{it.size()}}

Sample run:

groovy:000> ({it.grep(~/[o-rwetyui]+|[asdfghjkl]+|[zxcvbnm]+/).max{it.size()}})(['wrhuji', 'bxnzmmx', 'gllwssjjd', 'vnccbb', 'lrkjhgfdsa', 'tttttt'])
===> bxnzmmx

Note that the regular expression used by .grep() not requires anchoring, allowing to spare the grouping too:

groovy:000> ['ab', 'ac', 'bc', 'abc', 'aca', 'bbc'].grep ~/[ac]+|b+/
===> [ac, aca]
\$\endgroup\$
0
\$\begingroup\$

Japt -h, 18 bytes

;k_£D·bøXuÃä¦ dÃñÊ

Try it

;k_£D·bøXuÃä¦ dÃñÊ     :Implicit input of array
 k                     :Remove elements that return true
  _                    :When passed through the following function
   £                   :  Map each character X
;   D                  :    "QWERTYUIOP\nASDFGHJKL\nZXCVBNM"
     ·                 :    Split on newlines
      b                :    Index of first element that
       ø               :      Contains
        Xu             :      Uppercase X
          Ã            :  End map
           ä           :  Consecutive pairs of characters
            ¦          :  Reduced by checking for inequality
              e        :  Any true?
               Ã       :End filter
                ñ      :Sort by
                 Ê     :  Length
                       :Implicit output of last element
\$\endgroup\$
0
\$\begingroup\$

Perl 5 -l, 75 bytes

map$a[y///c]=$_,grep/^([weo-rtyui]+|[asdfghjkl]+|[zxcvbnm]+)$/,<>;say pop@a

Try it online!

\$\endgroup\$
0
\$\begingroup\$

K (ngn/k), 56 bytes

{x@*&|/&/''~^($`qwertyuiop`asdfghjkl`zxcvbnm)?\:x@:>#'x}

Try it online!

  • x@:>#'x sort the input (a list of strings) by their lengths, descending, updating x with the result
  • ($`qwertyuiop`asdfghjkl`zxcvbnm) generate a list of strings representing which letters are present on each row of a qwerty keyboard
  • ~^(...)?\:x check whether or not each letter of each input word is present or not in each keyboard line
  • &/'' determine if all letters of each input word are present in the same keyboard line or not
  • |/ check that there is at least one keyboard line containing all the letters of each input word
  • x@*& return the longest word in the input where the above criteria are met
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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