24
\$\begingroup\$

We are all used to the old-school telephone keypad, right? For reference, here is what it looks like:

Telephone Keybad


Given a String consisting only of lowercase ASCII letters and single spaces, your task is to return the number of taps one should make in order to type down the full String with a telephone keypad as the one above.

For those who are unfamiliar with this, here's how it works:

  • The key with the digit 2, for example, also has the string abc written down on it. In order to type a, you must press this key once, for b you must press twice and for c you must press thrice.

  • For consecutive letters that are on the same key, you must wait 1 second before pressing again. So, if you want to type cb, you must press 3 times for c, wait a second and then press twice for b, so still 5 taps.

  • The same applies for all other keys, except for a single space, which only requires 1 press. Also note that the keys 7 and 9 have four letters on them. The same algorithm is applied, the only difference being the number of letters. The strings corresponding to each key can be found in the image above (but lowercase), or in the following list, that contains all the characters you might receive:

    "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz", " "
    

Test Cases

Input  ->  Output  (Explanation)

"" -> 0                   (nothing should be tapped)
"water" -> 8              ("w,a,t" each require 1 tap (on keys 9, 2 and 8), "e" requires 2 taps (on key 3), "r" requires 3 taps (on key 7), 1+1+1+2+3 = 8)
"soap"  -> 9              (4+3+1+1)
"candela" -> 13           (3+1+2+1+2+3+1)
"code golf" -> 20         (3+3+1+2+1(for the space)+1+3+3+3)
"king of the hill" -> 33  (2+3+2+1+1+3+3+1+1+2+2+1+2+3+3+3)

Specs

  • Standard I/O rules and Default Loopholes apply.

  • You may only take input in your language's native String type. Output can either be an integer or a string representation of that integer.

  • This is , the shortest answer in every language wins.

\$\endgroup\$
  • \$\begingroup\$ Related, Inspired by this \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 11:03
  • \$\begingroup\$ Related. Related. \$\endgroup\$ – Shaggy Jun 28 '17 at 11:17
  • 2
    \$\begingroup\$ I think this would be a more interesting question if you did 1 tap per second, and had to wait 1 second, and counted seconds instead of taps. \$\endgroup\$ – Yakk Jun 28 '17 at 16:22
  • \$\begingroup\$ @Yakk That would be far too complicated \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 18:15
  • \$\begingroup\$ @Mr.Xcoder Are you sure though? I have seen the code-wizards here do impossible stuff in less space than a tweet. \$\endgroup\$ – J_F_B_M Jun 28 '17 at 22:54

21 Answers 21

11
\$\begingroup\$

JavaScript (ES6) 77 66 64 60 bytes

(Saved some bytes thanks to @Johan Karlsson and @Arnauld).

s=>[...s].map(l=>s=~~s+2+'behknquxcfilorvysz'.search(l)/8)|s

f=

s=>[...s].map(l=>s=~~s+2+'behknquxcfilorvysz'.search(l)/8)|s

console.log(f(''));                 //0
console.log(f('water'));            //8
console.log(f('soap'));             //9
console.log(f('candela'));          //13
console.log(f('code golf'));        //20
console.log(f('king of the hill')); //33

\$\endgroup\$
  • \$\begingroup\$ (s,t=0)=>[...s].map(l=>t+=(1+'behknquxcfilorvysz'.indexOf(l)/8|0)+1)&&t for 71 bytes \$\endgroup\$ – Johan Karlsson Jun 28 '17 at 11:39
  • \$\begingroup\$ Thanks, @JohanKarlsson, I figured the same thing while in the shower! Found another optimization to shave off 5 more bytes. \$\endgroup\$ – Rick Hitchcock Jun 28 '17 at 11:54
  • 6
    \$\begingroup\$ I found a purely arithmetical solution for 71 bytes: f=s=>[...s].map(c=>t+=((c=parseInt(0+c,36))>23?c+3:c&&~-c%3)%7%4+1,t=0)|t. \$\endgroup\$ – Neil Jun 28 '17 at 13:38
  • 1
    \$\begingroup\$ @Neil, while that may not be shorter, it is certainly cleverer. \$\endgroup\$ – Rick Hitchcock Jun 28 '17 at 13:40
  • 1
    \$\begingroup\$ @Neil You should post it. \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 13:43
7
\$\begingroup\$

05AB1E, 29 26 25 bytes

ð¢svA•22ā₂•S£ð«øðδKy.åƶOO

Try it online!

Explanation

ð¢                         # count spaces in input
  sv                       # for each char y in input
    A                      # push the lowercase alphabet
     •22ā₂•S               # push the base-10 digit list [3,3,3,3,3,4,3,4]
            £              # split the alphabet into pieces of these sizes
             ð«            # append a space to each
               ø           # transpose
                ðδK        # deep remove spaces
                   y.å     # check y for membership of each
                      ƶ    # lift each by their index in the list
                       O   # sum the list
                        O  # sum the stack
\$\endgroup\$
  • \$\begingroup\$ Sorry, but for an empty input this gives 10. It is fine elsewhere \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 12:01
  • \$\begingroup\$ @Mr.Xcoder: The empty string gives no output, but it's still wrong. Thanks for notifying, I'll fix it. \$\endgroup\$ – Emigna Jun 28 '17 at 12:06
  • 2
    \$\begingroup\$ It gives 10 on TIO. \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 12:06
  • \$\begingroup\$ @Mr.Xcoder: Yeah you have to explicitly give the empty string. No input is not the same as an empty string. It is a bit confusing I know. Fixed now though :) \$\endgroup\$ – Emigna Jun 28 '17 at 12:09
  • \$\begingroup\$ @Mr.Xcoder: Empty string input is given like this \$\endgroup\$ – Emigna Jun 28 '17 at 12:09
7
\$\begingroup\$

Python 2, 56 bytes

Uses the same algorithm as @RickHitchcock's Javascript solution

lambda x:sum('behknquxcfilorvysz'.find(c)/8+2for c in x)

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Interesting solution. How does it work for spaces, I don't get it >.<? \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 13:55
  • \$\begingroup\$ @Mr.Xcoder for anything not in the string '...'.find(c) returns -1. By adding 2 we get one keypress. \$\endgroup\$ – ovs Jun 28 '17 at 13:59
  • \$\begingroup\$ I knew it returned -1, but didn't realize you have a +2 after the boilerplate... Anyway, the shortest Python solution by far. \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 14:00
  • \$\begingroup\$ Oml, I happened to make the exact same solution after slowly golfing my program down, until I realized you posted it :( Nice job for finding this solution as well :) \$\endgroup\$ – Mario Ishac Jun 28 '17 at 18:14
5
\$\begingroup\$

Python 3, 69 67 65 64 bytes

1 byte thanks to Mr. Xcoder.

1 byte thanks to Felipe Nardi Batista.

lambda x:sum((ord(i)+~(i>"s"))%3+3*(i in"sz")+(i>" ")for i in x)

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Save one byte by replacing i==" " with i<"a", because you only receive letters and spaces \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 12:04
  • 4
    \$\begingroup\$ 61 minutes...too late! \$\endgroup\$ – Erik the Outgolfer Jun 28 '17 at 12:05
5
\$\begingroup\$

Dyalog APL, 37 bytes

+/⌈9÷⍨'adgjmptw behknqux~cfilorvy~'⍳⍞

Try it online!

How?

Get the ndex of every char of the input in the string 'adgjmptw behknqux~cfilorvy~' (s and z will default to 28), divide by 9, round up and sum.

\$\endgroup\$
  • \$\begingroup\$ You can use 'adgjmptw ' 'behknqux' 'cfilorvy' 'sz' to save some bytes \$\endgroup\$ – Cows quack Jun 28 '17 at 12:04
  • \$\begingroup\$ 49 bytes \$\endgroup\$ – Leaky Nun Jun 28 '17 at 12:26
  • \$\begingroup\$ @LeakyNun onice \$\endgroup\$ – Uriel Jun 28 '17 at 12:34
  • \$\begingroup\$ You can drop the space in the string \$\endgroup\$ – Cows quack Jun 28 '17 at 12:35
  • \$\begingroup\$ @Uriel wait, you don't need to count f← so it is 47 bytes \$\endgroup\$ – Leaky Nun Jun 28 '17 at 12:36
4
\$\begingroup\$

JavaScript (ES6), 71 bytes

f=
s=>[...s].map(c=>t+=((c=parseInt(0+c,36))>23?c+3:c&&~-c%3)%7%4+1,t=0)|t
<input oninput=o.textContent=f(this.value)><pre id=o>

Look no letter tables! I didn't quite understand @LeakyNun's formula so I came up with my own.

\$\endgroup\$
  • \$\begingroup\$ Pure arithmetic :) \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 14:01
  • \$\begingroup\$ What does s=>[...s] do why not just s=>s.map()... \$\endgroup\$ – Evan Carroll Jun 28 '17 at 16:34
  • 1
    \$\begingroup\$ @EvanCarroll s is a string, so you can't map it directly. ...s iterates over s, while [...s] converts the iteration into an array, effectively splitting s into an array of characters. \$\endgroup\$ – Neil Jun 28 '17 at 16:47
4
\$\begingroup\$

C, 211 196 bytes

First submission here...looks quite lengthy and I see that this is not an efficient approach, but at least it works :)

f(char*n){char*k=" abcdefghijklmnopqrstuvwxyz";int t[]={0,3,3,3,3,3,4,3,4};int l=0,s,j,i;while(*n){i=0;while(k[i]){if(k[i]==*n){s=0;for(j=0;s<i-t[j];s+=t[j++]);*n++;l+=(!i?1:i-s);}i++;}}return l;}

Ungolfed version:

int f(char *n){
  char *k=" abcdefghijklmnopqrstuvwxyz";
  int t[]={0,3,3,3,3,3,4,3,4};
  int l=0,s,j,i;
  while(*n){                          // loop through input characters
    i=0;
    while(k[i]){
      if(k[i]==*n){                   // find matching char in k
        s=0;
        for(j=0;s<i-t[j];s+=t[j++]);  // sum up the "key sizes" up to the key found
        *n++;
        l+=(!i?1:i-s);                // key presses are i-s except for space (1)
      }
      i++;
    }
  }
  return l;
}
\$\endgroup\$
  • \$\begingroup\$ *(k+i) can be k[i]. \$\endgroup\$ – CalculatorFeline Jun 28 '17 at 23:36
  • \$\begingroup\$ You can cut the space after an * (e.g. char*n), and add your declarations to your empty for statement (instead of int s=0,j=0;(for(; you'd have for(int s=0,k=0;) and instead of i==0 use !i \$\endgroup\$ – Tas Jun 29 '17 at 1:05
  • \$\begingroup\$ Thank you for those hints. I couldn't put s into the for loop because I use it later on, but I put the int declarations together and used assignemts where I needed them. \$\endgroup\$ – dbuchmann Jun 29 '17 at 6:59
  • \$\begingroup\$ Yay a fellow C golfer! Anyways, some pointers: for loops are strictly better than while loops in almost all situation - take advantage of the free semicolons, particularly in the iteration expression. Use commas instead of semicolons in most places, this allows you to get away with not having curly braces in most places. There are other optimizations but they are more reliant on which version of C you compile to. \$\endgroup\$ – dj0wns Jul 5 '17 at 11:16
4
\$\begingroup\$

Haskell - 74 71 62 bytes

Edit: removed 3 bytes by using a list comprehension instead of filter

Edit: Save 9 bytes thanks to Siracusa, Laikoni and Zgarb!

f=sum.(>>= \x->1:[1|y<-"bcceffhiikllnooqrrsssuvvxyyzzz",y==x])

Usage

λ> f "candela"
13
λ>

Try it online!

\$\endgroup\$
  • \$\begingroup\$ What is the purpose of the duplicate letters? \$\endgroup\$ – Mr. Xcoder Jun 28 '17 at 18:19
  • \$\begingroup\$ @Mr.Xcoder It's used to count the taps, I will add an explanation. \$\endgroup\$ – Henry Jun 28 '17 at 18:21
  • \$\begingroup\$ You can save one byte by rewriting f to f=length.(=<<)(\x->x:[y|y<-l,y==x]), where (=<<) is concatMap here. \$\endgroup\$ – siracusa Jun 28 '17 at 20:47
  • \$\begingroup\$ And another one with going back to filter: f=length.(=<<)(\x->x:filter(==x)l) \$\endgroup\$ – siracusa Jun 28 '17 at 20:51
  • 1
    \$\begingroup\$ As you use l only once, it can be inlined. \$\endgroup\$ – Laikoni Jun 28 '17 at 22:07
3
\$\begingroup\$

Jelly, 25 bytes

Øaḟ⁾sz©;⁶s3Z®ṭċ@€€⁸×"J$ẎS

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Clojure, 82 76 bytes

#(apply +(for[c %](+(count(filter #{c}"bcceffhiikllnooqrrsssuvvxyyzzz"))1)))

Oh it is simpler to just filter and count than use frequencies. Original:

#(apply +(count %)(for[c %](get(frequencies"bcceffhiikllnooqrrsssuvvxyyzzz")c 0)))

The string encodes how many times more than just once you need to press the key for a given character :)

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

Python 3, 91 bytes

lambda x:sum(j.find(i)+1for j in' !abc!def!ghi!jkl!mno!pqrs!tuv!wxyz'.split('!')for i in x)

Try it online!

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

Python 3, 60 bytes

Probably sub-optimal, as this is my first golf ever in Python.

lambda x:sum((ord(i)-8)%3.15//1+3*(i>'y')+(i>' ')for i in x)

Try it online!

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

Retina, 46 36 bytes

Thanks to CalculatorFeline for saving 6 bytes.

s|z
,c
[cfilorvy]
,b
[behknqux]
,,
.

Try it online!

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

Java, 95 73 bytes

a->a.chars().map(b->1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112)).sum()

Thanks to Kevin Cruijssen for making the function a lambda expression (where a is of type String). 95 bytes became 73 bytes!

A lambda expression sums up the press count of each character using map(). map() converts each character (ASCII in lower case range is 97-122) in the stream to the appropriate value (looks like simple saw wave, but taking into account both 4 cycles is annoying) using this math: 1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112). Here's a desmos graph of that model.

\$\endgroup\$
  • \$\begingroup\$ The loophole list says not to post code snippets even though it looks like everyone so far has done that. Either way, my full program is 130 bytes. Here it is: interface Z{static void main(String a){System.out.print(a.chars().map(b->1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112)).sum());}} \$\endgroup\$ – Adam Mendenhall Jun 28 '17 at 21:19
  • 1
    \$\begingroup\$ Welcome to PPCG! You're indeed right that snippets aren't allowed, but the default is program or function. And with Java 8 you can use lambdas. So in this case a->{return a.chars().map(b->1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112)).sum();} is allowed. And since it's a single return statement, a->a.chars().map(b->1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112)).sum() (73 bytes) would be your answer. Also, here is a TryItOnline-link of your answer you might want to add to your answer. Again: welcome, and nice answer. +1 from me. \$\endgroup\$ – Kevin Cruijssen Jun 29 '17 at 7:56
  • 2
    \$\begingroup\$ Some things to note about lambdas. You don't have to count f= nor the leading semi-colon ;. And you also don't have to add the type of the parameter as long as you mention what the type is (so instead of (String a)-> you can use a-> and mention that input a is a String in your answer). Oh, and Tips for golfing in Java and Tips for golfing in <all languages> might be interesting to read, in case you haven't yet. \$\endgroup\$ – Kevin Cruijssen Jun 29 '17 at 8:23
1
\$\begingroup\$

Mathematica, 83 bytes

c=Characters;Tr[Tr@Mod[c@"bc1def1ghi1jkl1mno1pqrstuv1wxyz "~Position~#,4]+1&/@c@#]&
\$\endgroup\$
  • \$\begingroup\$ It seems to be generally accepted that Mathematica answers are allowed to use lists of characters for string variables, such as the input to this function. (Also is there an a missing at the start of "bc1..."?) \$\endgroup\$ – Greg Martin Jun 28 '17 at 18:43
  • \$\begingroup\$ this is code golf.this gives the right result without a."Tr " does the job \$\endgroup\$ – J42161217 Jun 28 '17 at 18:49
1
\$\begingroup\$

QBIC, 94 bytes

[_l;||_SA,a,1|p=p-(instr(@sz`,B)>0)-(instr(@cfilorvy`+C,B)>0)-(instr(@behknqux`+C+D,B)>0)+1}?p

Explanation

[    |      FOR a = 1 TO
 _l |         the length of
   ;            the input string (A$)
_SA,a,1|    Take the a'th char of A$ and assign it to B$
p=p         p is our tap-counter, and in each iteration it gets increased by the code below
            which consist of this pattern:
                instr(@xyz`,B)>0    where 
                - instr tests if arg 2 is in arg 1 (it either returns 0 or X where X is the index of a2 in a1)
                - @...` defines the letters we want to test as arg1
                - B is the current letter to count the taps for
            Each of these blocks adds 1 tap to the counter, and each block has the letters of its level
            (4-taps, 3-taps or 2-taps) and the level 'above' it.
    -(instr(@sz`,B)>0)              <-- letters that require 4 taps
    -(instr(@cfilorvy`+C,B)>0)      <-- 3 or 4 taps
    -(instr(@behknqux`+C+D,B)>0)    <-- 2, 3,or 4 taps
    +1                              <-- and always a 1-tap
}           NEXT
?p          PRINT the number of taps
\$\endgroup\$
1
\$\begingroup\$

Bash, 69 68 bytes

bc<<<`fold -1|tr "\n "adgjmptwbehknquxcfilorvysz +[1*9][2*8][3*8]44`

Try it online!

Folds one char per line, transliterates each newline with +, each space with 1 and each letter with the corresponding number of pushes. bc does the sum.

\$\endgroup\$
  • \$\begingroup\$ on your machine you may need bc <(fold -1|tr "\n "adgjmptwbehknquxcfilorvysz +[1*9][2*8][3*8]44;echo 0) \$\endgroup\$ – marcosm Jun 28 '17 at 18:36
1
\$\begingroup\$

C, 92 88 bytes

c,n;f(char*s){n=0;while(c=*s++)n+=(c=='s')+3*(c>'y')+1+(c+1+(c<'s'))%3-(c<33);return n;}
\$\endgroup\$
  • \$\begingroup\$ you may use s=n to replace return n, and combine s++; with c=*s. It could be 9 bytes shorter. \$\endgroup\$ – Keyu Gan Jun 29 '17 at 3:34
  • \$\begingroup\$ @KeyuGan s=n wouldn't work, since s is a local. And *s=n wouldn't work since there are only CHAR_BIT bits in *s, which wouldn't be enough for some messages. But you're right about the s++. Thanks. \$\endgroup\$ – Ray Jul 3 '17 at 21:50
1
\$\begingroup\$

APL (Dyalog), 36 bytes

{+/(3×⍵∊'sz'),1+3|¯1+⍵⍳⍨819⌶⎕A~'SZ'}

Try it online!

Finds the mod-3 indices in the alphabet without S and Z. Since space, S, and Z are not found, they "have" index 25 (one more than the max index), which is good for space. Then we just need to add 3 for each S or Z.

{ anonymous function where the argument is represented by :

⎕A~'SZ' the uppercase Alphabet, except for S and Z

819⌶ lowercase

⍵⍳⍨ the ɩndices of the argument in that

¯1+ add negative one

3| mod-3

1+ add one (this converts all 0-mods to 3)

(), prepend:

  ⍵∊'sz' Boolean where the argument is either s or z

   multiply by 3

+/ sum

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

C (gcc), 75 77 bytes

n,b;f(char*a){for(n=0;b=*a++;)n+=b<'s'?--b%3+(b>31):""[b-'s'];a=n;}

The unprintable string "" is a table of 04 01 02 03 01 02 03 04.

Try it online!

\$\endgroup\$
  • \$\begingroup\$ @ceilingcat thanks! \$\endgroup\$ – Keyu Gan Aug 1 at 7:24
1
\$\begingroup\$

Pip, 100 90 bytes

a:qb:["abc""def""ghi""jkl""mno""pqrs""tuv""wxyz"s]Fc,#a{Fd,#b{e:((bd)@?(ac))e<4?i+:e+1x}}i

Check each character of the input for a match in each element of b. The index of that match plus 1 gets added to the total.

Try it online!

\$\endgroup\$

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.