14
\$\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\$
2
  • \$\begingroup\$ May the I/O be lowercase, or is uppercase mandatory? \$\endgroup\$ Commented Nov 3, 2020 at 14:52
  • 1
    \$\begingroup\$ @KevinCruijssen You can do lower case. \$\endgroup\$
    – Wheat Wizard
    Commented Nov 3, 2020 at 15:38

10 Answers 10

5
\$\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\$
3
\$\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\$
7
  • \$\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
    Commented Sep 24, 2017 at 17:41
  • \$\begingroup\$ I think it disagrees again? F doesn't seem to be an allowed output anymore... \$\endgroup\$ Commented Sep 24, 2017 at 18:02
  • \$\begingroup\$ @EriktheOutgolfer Ah okay I didn't update that remark after FCM corrected the test case \$\endgroup\$
    – hyperneutrino
    Commented Sep 24, 2017 at 18:03
  • \$\begingroup\$ Somehow Mathematica doesn't have a bultin for keyboard rows. \$\endgroup\$
    – Razetime
    Commented Sep 19, 2020 at 15:25
  • 1
    \$\begingroup\$ @Razetime ah, I see. yeah finding something mathematica doesn't just have a builtin for is always pretty rare :D \$\endgroup\$
    – hyperneutrino
    Commented Sep 19, 2020 at 16:54
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\$

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\$

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\$
7
  • \$\begingroup\$ Gives me the wrong result for TEXT. \$\endgroup\$
    – Ian H.
    Commented Sep 24, 2017 at 17:36
  • \$\begingroup\$ @IanH. It gives me 'R' which is what OP asked for \$\endgroup\$ Commented Sep 24, 2017 at 17:39
  • \$\begingroup\$ Didn't see that was changed in the task, my bad. \$\endgroup\$
    – Ian H.
    Commented Sep 24, 2017 at 17:41
  • 1
    \$\begingroup\$ 431 bytes. \$\endgroup\$ Commented Sep 24, 2017 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
    Commented Sep 24, 2017 at 21:18
1
\$\begingroup\$

Charcoal, 79 bytes

≔⪪”$±=K≕⦃Vj@η⟲.w\`o:7➙1”¶ηFθF³F⌕A§ηκι⊞υ⟦λκ⟧IE²∕ΣEυ§λιL觧η⁺·⁵∕ΣEυ⊟ιLθ⁺·⁵∕ΣEυ⊟ιLθ

Try it online!

Neil's golf using string functions.

Charcoal, 107 bytes

P”$±=K≕⦃Vj@η⟲.w\`o:7➙1”≔⟦⟧α≔⟦⟧βFχ«F³«JικFθ«¿⁼λKK«⊞α⎇›ⅉ⁰⁺∕¹¦³ⅈⅈ⊞βⅉ»»»»J⌊∕⁺Σα·⁵Lθ⌈∕⁺Σβ·⁵Lθ≔KKχ⎚χ¶I∕ΣαLθ¶I∕ΣβLθ

Try it online!

Draws the keyboard on the canvas and finds the average that way.

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

05AB1E, 44 bytes

žVIšεεžVsδkDZkDŠ3/+à‚]ćøÅA`UòDžVsèŠè€θDX.xkè

Not too happy with it, but it works.. :/ Can definitely be golfed some more using a different approach.

I/O in lowercase.

Try it online or verify all test cases.

Explanation:

Step 1: create a list with all the coordinate-values, and convert the input in a similar matter as well:

žV                     # Push builtin ["qwertyuiop","asdfghjkl","zxcvbnm"]
  Iš                   # Prepend the input-string to this list
    ε                  # Map over each string:
     ε                 #  Inner map over each individual character:
      žV               #   Push ["qwertyuiop","asdfghjkl","zxcvbnm"] again
        s              #   Swap so the current character is at the top of the stack
         δk            #   Get the index of this character in each of the three strings
                       #   (or -1 if it's not found)
           D           #   Duplicate this list of indices
            Z          #   Get the largest index (without popping)
             k         #   Get the index of this largest index in the triplet
              D        #   Duplicate this index
               Š       #   Triple swap the three values on the stack (a,b,c→c,a,b)
                3/     #   Divide it by 3
                  +    #   Add it to each index in the list we duplicated
                   à   #   Pop and push the maximum again
                    ‚  #   And pair it with the duplicated & triple-swapped index
    ]                  # Close the nested maps

Try just this first step.

Step 2: get the average coordinate of the input:

ć                      # Extract head; pop and push remainder-list and first item separated
 ø                     # Zip/transpose; swapping rows/columns
  ÅA                   # Get the arithmetic mean of both inner triplets

Try just the first two steps.

Step 3: get the coordinate closest to this average coordinate, and convert it back to a character to output:

`                      # Pop and push both separated to the stack
 U                     # Pop and store the second one in variable `X`
  ò                    # Round the first value to the nearest integer
   D                   # Duplicate this integer
    žV                 # Push ["qwertyuiop","asdfghjkl","zxcvbnm"] yet again
      s                # Swap so the integer is at the top of the stack
       è               # Index it into the list of strings
        Š              # Triple-swap the values on the stack (a,b,c→c,a,b)
         è             # Index the duplicated integer into the remainder-list
          €θ           # Leave the last value of each inner pair
            D          # Duplicate the list
             X.x       # Pop the copy and leave the value closest to value `X`
                k      # Get the index of this value in the duplicated list
                 è     # And index it into the string we triple-swapped
                       # (after which this character is output implicitly as result)
\$\endgroup\$
1
\$\begingroup\$

Jelly, 33 31 bytes

Æịµ÷3Ċ+
ØQœiⱮÇÆmØQŒṪ¤Çạ¥ÐṂœịØQX

Try it online!

Uses some features that were presumably not implemented when HyperNeutrino wrote his solution, so considering this only beats it by 4 6 bytes, I wouldn't be surprised if a modernized version of his would come out shorter--but I started writing this before I even noticed his existed.

Æịµ÷3Ḟ+                    Helper link (monadic):
Æị                         convert list to complex,
  µ   +                    add the complex number to
     Ċ                     the imaginary part of
   ÷3                      its quotient by 3.

ØQœiⱮÇÆmØQŒṪ¤Çạ¥ÐṂœịØQX    Main link:
ØQœiⱮ                      multidimensional indices of input in QWERTY layout,
     Ç                     apply helper link,
      Æm                   average;
        ØQŒṪ¤              for all multidimensional indices in the layout
                ÐṂ         keep those with the smallest
              ạ¥           absolute difference between the average and
             Ç             their result from the helper link;
                  œịØQ     index back into QWERTY
                      X    and choose one key at random.
\$\endgroup\$
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\$
1
  • \$\begingroup\$ Fixed! works for all test cases. \$\endgroup\$
    – ZaMoC
    Commented Sep 24, 2017 at 18:58

Your Answer

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

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