8
\$\begingroup\$

Inspired by this chat message

Your task will be to take a word and find the average position of its letters on the keyboard as a letter.

Keyboard Layout

Since layouts vary from keyboard to keyboard, we will be using a standard based of of my own keyboard in this question.

The keyboard has 3 rows, the top row from left to right contains the keys

QWERTYUIOP

The second row contains the letters

ASDFGHJKL

The final row contains

ZXCVBNM

Each letter is 1 unit horizontal from its neighbor to the left. This means that W is 1 away from Q and E is 1 away from W and so on.

The keys at the beginning of each row have the positions:

Q : 0,0
A : 1/3,1
Z : 2/3,2

This means that the rows are separated one unit vertically and the bottom two rows are shifted by a third from the row above them.


You should take a word as input and output the letter that is closest to the average position of the letters in its word. The average of a set of vectors is

(average x value, average y value)

When two keys are equidistant from the average you may output either as the "closest" key.

This is so answers will be scored in bytes with fewer bytes being better.

Example solution

Let's calculate the average of APL.

We convert each letter to a vector

A -> (1/3,1)
P -> (9,0)
L -> (8 1/3,1)

We add these up the three vectors to get (17 2/3, 2). We then divide each coordinate by 3 (The number of letters in the word) to get (5 8/9, 2/3).

The closest letter to (5 8/9, 2/3) is J at (6 1/3,1) so our result is J.

Test Cases

APL  -> J
TEXT -> R
PPCG -> J
QQQQ -> Q
ZZZZ -> Z
PPPP -> P
MMMM -> M
QQSS -> A or W
\$\endgroup\$
3
\$\begingroup\$

C# (.NET Core), 250 + 13 bytes

+13 bytes for using System;

n=>{var a=new[]{"QWERTYUIOP","ASDFGHJKL","ZXCVBNM"};float x=0,y=0;int i=0,j=0,l=n.Length;foreach(char c in n){for(i=0,j=0;i<2;){if(a[i][j]==c)break;if(++j>=a[i].Length){i++;j=0;}}x+=j;y+=i;}return a[(int)Math.Round(y/3)][(int)Math.Round(x/l+y/l/3)];}

Try it online!

Little sidenote: This outputs F for TEXT, since that was the original desired output.
Outputting R instead of F was changed after this answer was posted.

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 166 bytes

f=
s=>[...s].map(c=>(h+=c=s.search(c),v+=c%3,l++),h=v=l=0,s='QAZWSXEDCRFVTGBYHNUJMIKKOLLP')&&[...s].map((c,i)=>(i=(i-h/l)*(i-h/l)+(i=i%3-v/l)*i*9,i)<m&&(m=i,r=c),m=9)&&r
<input oninput=o.textContent=/^[A-Z]+$/.test(this.value)?f(this.value):``><pre id=o>

6 bytes could be saved by switching to ES7. Previous 131-byte solution used a simplistic distance check which is no longer acceptable.

f=
s=>([...s].map(c=>(h+=c=s.search(c),v+=c%3,l++),h=v=l=0,s='QAZWSXEDCRFVTGBYHNUJMIKKOLLP'),v=(v/l+.5|0),h=((h/l-v)/3+.5|0),s[h*3+v])
<input oninput=o.textContent=/^[A-Z]+$/.test(this.value)?f(this.value):``><pre id=o>

\$\endgroup\$
2
\$\begingroup\$

Python 3, 130 bytes

lambda w,d={'QAZWSXEDCRFVTGBYHNUJMIKOOLPP'[i]:i%3*1j+i/3for i in range(28)}:min(d,key=lambda c:abs(d[c]-sum(map(d.get,w))/len(w)))

Try it online!

d={'QAZWSXEDCRFVTGBYHNUJMIKOOLPP'[i]:i%3*1j+i/3for i in range(28)} constructs the mapping from letters to points (represented as complex numbers, (x+y*1j)).

As for the lambda body, sum(map(d.get,w))/len(w) computes the average position of word w, and putting that in min(d,key=lambda c:abs(d[c]-…)) finds the closest letter to that position. (For complex numbers, abs(A-B) corresponds to the Euclidean distance between (A.real, A.imag) and (B.real, B.imag).)

\$\endgroup\$
2
\$\begingroup\$

Java, 257 243 242 237 bytes

char h(String s){int l=s.length(),i=l+28;s="QAZWSXEDCRFVTGBYHNUJMIK<OL>P"+s;float d=9,x=0,y=0,e;for(;i>28;y+=(e=s.indexOf(s.charAt(--i)))%3/l,x+=e/3/l);for(;i-->0;)if((e=(x-i/3f)*(x-i/3f)+(y-i%3)*(y-i%3))<d){d=e;l=i;}return s.charAt(l);}

Saved 14 bytes - the distance away from the best key will be less than 3 units

\$\endgroup\$
1
\$\begingroup\$

Jelly, 37 bytes

ØQi€µT÷3Ḣ+Ṁ;T
Ç€ZÆmðạ²SðЀØAÇ€¤iṂ$ịØA

Try it online!

lol way too long

Explanation

ØQi€µT÷3Ḣ+Ṁ;T            Helper Link; compute the position of a key
   €                     For each row of
ØQ                       ["QWERTYUIOP", "ASDFGHJKL", "ZXCVBNM"] (hooray for builtins)
  i                      Find the first occurrence of the argument
    µ                    Start a new monadic chain
     T                   List of indices of truthy values; singleton list with the row of the key
      ÷                  Divide the index by
       3                 3
        Ḣ                Take the first element
         +               Add it to the original list
          Ṁ              Take the maximum (the adjusted horizontal position of the key)
           ;             Append
            T            The index of the truthy value (the row)
Ç€ZÆmðạ²SðЀØAÇ€¤iṂ$ịØA  Main Link
 €                       For each character in the input
Ç                        Compute its position using the helper link
  Z                      Zip (all of the horizontal positions are in the first list; all of the vertical positions are in the second list)
   Æm                    Take the arithmetic mean (of each sublist)
     ðạ²Sð               Dyadic chain to compute the distance (squared) between two coordinates
      ạ                  Take the absolute difference between each coordinate value (auto-vectorization)
       ²                 Square each value
        S                Take the sum (returns the distance squared but for comparison that's fine)
          Ѐ             Take the distance between the mean position and each element in
            ØAÇ€¤        [Nilad:] the positions of each character in the uppercase alphabet
               €         For each character in
            ØA           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
              Ç          Compute its position
                 iṂ$     Find the index of the minimum (closest)
                 i       First occurrence of             in the list of distances
                  Ṃ                          the minimum
                    ị    Index back into
                     ØA  The alphabet
\$\endgroup\$
  • \$\begingroup\$ @downvoter the issue with the test case has been fixed; please remove your downvote or explain why you wish to downvote my answer \$\endgroup\$ – HyperNeutrino Sep 24 '17 at 17:41
  • \$\begingroup\$ I think it disagrees again? F doesn't seem to be an allowed output anymore... \$\endgroup\$ – Erik the Outgolfer Sep 24 '17 at 18:02
  • \$\begingroup\$ @EriktheOutgolfer Ah okay I didn't update that remark after FCM corrected the test case \$\endgroup\$ – HyperNeutrino Sep 24 '17 at 18:03
1
\$\begingroup\$

Java (OpenJDK 8), 452 431 424 400 389 324 322 296 285 281 276 274 260 258 257 bytes

Something to start golfing from

s->{String c="QWERTYUIOPASDFGHJKL;ZXCVBNM";int i=0,r=0,l=0;double x=0,y=0,D=99,t,f=s.length();for(;i<f;x+=l%10+l/10/3d,y+=l/10)l=c.indexOf(s.charAt(i++));for(;r<27;r++)if(D>(t=Math.pow(x/f-r/10/3d-r%10,2)+Math.pow(y/f-r/10,2))){D=t;l=r;}return c.charAt(l);}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Gives me the wrong result for TEXT. \$\endgroup\$ – Ian H. Sep 24 '17 at 17:36
  • \$\begingroup\$ @IanH. It gives me 'R' which is what OP asked for \$\endgroup\$ – Roberto Graham Sep 24 '17 at 17:39
  • \$\begingroup\$ Didn't see that was changed in the task, my bad. \$\endgroup\$ – Ian H. Sep 24 '17 at 17:41
  • 1
    \$\begingroup\$ 431 bytes. \$\endgroup\$ – Jonathan Frech Sep 24 '17 at 17:47
  • \$\begingroup\$ I hadn't seen this answer when I posted mine, should I have suggested mine as a comment or are multiple answers for the same language ok? \$\endgroup\$ – Tahg Sep 24 '17 at 21:18
0
\$\begingroup\$

Mathematica, 234 bytes

(r=(K=Round)@Mean[If[(w=First[q=#&@@Position[(s=X/@{"QWERTYUIOP","ASDFGHJKL","ZXCVBNM"}),#]])==1,q=q-1,If[w==2,q=q+{-1,-2/3},q=q+{-1,-1/3}]]&/@(X=Characters)@#];z=If[(h=#&@@r)==0,r=r+1,If[h==1,r=r+{1,2/3},r=r+{1,1/3}]];s[[##]]&@@K@z)&  
\$\endgroup\$
  • \$\begingroup\$ Fixed! works for all test cases. \$\endgroup\$ – J42161217 Sep 24 '17 at 18:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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